es31fPrimitiveBoundingBoxTests.cpp revision c215aaa83047ebbaabafef7acd71275a256da6ab
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
49namespace deqp
50{
51namespace gles31
52{
53namespace Functional
54{
55namespace
56{
57
58namespace StateQueryUtil = ::deqp::gls::StateQueryUtil;
59
60struct 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
73float& 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
82const float& BoundingBox::getComponentAccess (int ndx) const
83{
84	return const_cast<BoundingBox*>(this)->getComponentAccess(ndx);
85}
86
87struct ProjectedBBox
88{
89	tcu::Vec3	min;
90	tcu::Vec3	max;
91};
92
93static 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
106static 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
124class InitialValueCase : public TestCase
125{
126public:
127					InitialValueCase	(Context& context, const char* name, const char* desc);
128
129	void			init				(void);
130	IterateResult	iterate				(void);
131};
132
133InitialValueCase::InitialValueCase (Context& context, const char* name, const char* desc)
134	: TestCase(context, name, desc)
135{
136}
137
138void 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
144InitialValueCase::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
183class QueryCase : public TestCase
184{
185public:
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
198private:
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
207QueryCase::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
214void 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
220QueryCase::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.glPrimitiveBoundingBoxEXT(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
267bool 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
453class BBoxRenderCase : public TestCase
454{
455public:
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
478protected:
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
563private:
564	std::vector<IterationConfig>	m_iterationConfigs;
565	int								m_iteration;
566};
567
568BBoxRenderCase::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
600BBoxRenderCase::~BBoxRenderCase (void)
601{
602	deinit();
603}
604
605void 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
700void BBoxRenderCase::deinit (void)
701{
702	m_program.clear();
703	m_vbo.clear();
704	m_fbo.clear();
705}
706
707BBoxRenderCase::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
727BBoxRenderCase::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
772tcu::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
801void 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.primitiveBoundingBoxEXT(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.primitiveBoundingBoxEXT(-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
869const 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
918class GridRenderCase : public BBoxRenderCase
919{
920public:
921					GridRenderCase					(Context& context, const char* name, const char* description, deUint32 flags);
922					~GridRenderCase					(void);
923
924private:
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
941GridRenderCase::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
947GridRenderCase::~GridRenderCase (void)
948{
949}
950
951void 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
963std::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
1010std::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
1034std::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
1115std::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
1152std::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
1208GridRenderCase::IterationConfig GridRenderCase::generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const
1209{
1210	return generateRandomConfig(0xDEDEDEu * (deUint32)iteration, renderTargetSize);
1211}
1212
1213void 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
1248void 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
1274void 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
1374class LineRenderCase : public BBoxRenderCase
1375{
1376public:
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
1385private:
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	};
1406
1407	void				init							(void);
1408
1409	std::string			genVertexSource					(void) const;
1410	std::string			genFragmentSource				(void) const;
1411	std::string			genTessellationControlSource	(void) const;
1412	std::string			genTessellationEvaluationSource	(void) const;
1413	std::string			genGeometrySource				(void) const;
1414
1415	IterationConfig		generateConfig					(int iteration, const tcu::IVec2& renderTargetSize) const;
1416	void				getAttributeData				(std::vector<tcu::Vec4>& data) const;
1417	void				renderTestPattern				(const IterationConfig& config);
1418	void				verifyRenderResult				(const IterationConfig& config);
1419
1420	tcu::IVec2			getNumberOfLinesRange			(int queryAreaBegin, int queryAreaEnd, float patternStart, float patternSize, int viewportArea, QueryDirection queryDir) const;
1421	deUint8				scanRow							(const tcu::ConstPixelBufferAccess& access, int row, int rowBegin, int rowEnd, const tcu::IVec2& numLines, int& floodCounter) const;
1422	deUint8				scanColumn						(const tcu::ConstPixelBufferAccess& access, int column, int columnBegin, int columnEnd, const tcu::IVec2& numLines, int& floodCounter) const;
1423	bool				checkAreaNumLines				(const tcu::ConstPixelBufferAccess& access, const tcu::IVec4& area, int& floodCounter, int componentNdx, const tcu::IVec2& numLines) const;
1424	tcu::IVec2			getNumMinimaMaxima				(const tcu::ConstPixelBufferAccess& access, int componentNdx) const;
1425	bool				checkLineWidths					(const tcu::ConstPixelBufferAccess& access, const tcu::IVec2& begin, const tcu::IVec2& end, int componentNdx, int& floodCounter) const;
1426	void				printLineWidthError				(const tcu::IVec2& pos, int detectedLineWidth, const tcu::IVec2& lineWidthRange, bool isHorizontal, int& floodCounter) const;
1427
1428	const int			m_patternSide;
1429	const bool			m_isWideLineCase;
1430	const int			m_wideLineLineWidth;
1431};
1432
1433LineRenderCase::LineRenderCase (Context& context, const char* name, const char* description, deUint32 flags)
1434	: BBoxRenderCase		(context, name, description, 12, flags)
1435	, m_patternSide			(12)
1436	, m_isWideLineCase		((flags & LINEFLAG_WIDE) != 0)
1437	, m_wideLineLineWidth	(5)
1438{
1439}
1440
1441LineRenderCase::~LineRenderCase (void)
1442{
1443}
1444
1445void LineRenderCase::init (void)
1446{
1447	m_testCtx.getLog()
1448		<< tcu::TestLog::Message
1449		<< "Rendering line pattern to " << ((m_renderTarget == RENDERTARGET_DEFAULT) ? ("default frame buffer") : ("fbo")) << ".\n"
1450		<< "Vertical lines are green, horizontal lines blue. Using additive blending.\n"
1451		<< "Line segments are in random order, varying pattern size and location for each iteration.\n"
1452		<< "Marking all discardable fragments (fragments outside the bounding box) with a fully saturated red channel."
1453		<< tcu::TestLog::EndMessage;
1454
1455	if (m_isWideLineCase)
1456	{
1457		glw::GLfloat lineWidthRange[2] = {0.0f, 0.0f};
1458		m_context.getRenderContext().getFunctions().getFloatv(GL_ALIASED_LINE_WIDTH_RANGE, lineWidthRange);
1459
1460		if (lineWidthRange[1] < (float)m_wideLineLineWidth)
1461			throw tcu::NotSupportedError("Test requires line width " + de::toString(m_wideLineLineWidth));
1462	}
1463
1464	BBoxRenderCase::init();
1465}
1466
1467std::string LineRenderCase::genVertexSource (void) const
1468{
1469	std::ostringstream	buf;
1470
1471	buf <<	"#version 310 es\n"
1472			"in highp vec4 a_position;\n"
1473			"in highp vec4 a_color;\n"
1474			"out highp vec4 vtx_color;\n"
1475			"uniform highp vec4 u_posScale;\n"
1476			"uniform highp float u_lineWidth;\n"
1477			"\n";
1478	if (!m_hasTessellationStage)
1479	{
1480		DE_ASSERT(m_useGlobalState);
1481		buf <<	"uniform highp vec4 u_primitiveBBoxMin;\n"
1482				"uniform highp vec4 u_primitiveBBoxMax;\n"
1483				"\n"
1484				"flat out highp float v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize;\n"
1485				"flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n"
1486				"flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n"
1487				"\n";
1488	}
1489	buf <<	"void main()\n"
1490			"{\n"
1491			"	highp vec2 patternOffset = u_posScale.xy;\n"
1492			"	highp vec2 patternScale = u_posScale.zw;\n"
1493			"	gl_Position = vec4(a_position.xy * patternScale + patternOffset, a_position.z, a_position.w);\n"
1494			"	vtx_color = a_color;\n";
1495	if (!m_hasTessellationStage)
1496	{
1497		DE_ASSERT(m_useGlobalState);
1498		buf <<	"\n"
1499				"	v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize = u_lineWidth;\n"
1500				"	v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin =\n"
1501				"	    min(vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMin.w,\n"
1502				"	        vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMax.w);\n"
1503				"	v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax =\n"
1504				"	    min(vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMin.w,\n"
1505				"	        vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMax.w);\n";
1506	}
1507	buf <<	"}\n";
1508
1509	return buf.str();
1510}
1511
1512std::string LineRenderCase::genFragmentSource (void) const
1513{
1514	const char* const	colorInputName = (m_hasGeometryStage) ? ("geo_color") : (m_hasTessellationStage) ? ("tess_color") : ("vtx_color");
1515	std::ostringstream	buf;
1516
1517	buf <<	"#version 310 es\n"
1518			"in mediump vec4 " << colorInputName << ";\n"
1519			"layout(location = 0) out mediump vec4 o_color;\n"
1520		<<	genShaderFunction(SHADER_FUNC_INSIDE_BBOX)
1521		<<	"\n"
1522			"void main()\n"
1523			"{\n"
1524			"	mediump vec4 baseColor = " << colorInputName << ";\n"
1525			"	mediump float redChannel;\n"
1526			"	if (fragmentInsideTheBBox(gl_FragCoord.z))\n"
1527			"		redChannel = 0.0;\n"
1528			"	else\n"
1529			"		redChannel = 1.0;\n"
1530			"	o_color = vec4(redChannel, baseColor.g, baseColor.b, baseColor.a);\n"
1531			"}\n";
1532
1533	return buf.str();
1534}
1535
1536std::string LineRenderCase::genTessellationControlSource (void) const
1537{
1538	std::ostringstream	buf;
1539
1540	buf <<	"#version 310 es\n"
1541			"#extension GL_EXT_tessellation_shader : require\n"
1542			"#extension GL_EXT_primitive_bounding_box : require\n"
1543			"layout(vertices=2) out;"
1544			"\n"
1545			"in highp vec4 vtx_color[];\n"
1546			"out highp vec4 tess_ctrl_color[];\n"
1547			"uniform highp float u_tessellationLevel;\n"
1548			"uniform highp vec4 u_posScale;\n"
1549			"uniform highp float u_lineWidth;\n";
1550
1551	if (!m_calcPerPrimitiveBBox)
1552	{
1553		buf <<	"uniform highp vec4 u_primitiveBBoxMin;\n"
1554				"uniform highp vec4 u_primitiveBBoxMax;\n";
1555	}
1556
1557	buf <<	"patch out highp float vp_bbox_expansionSize;\n"
1558			"patch out highp vec3 vp_bbox_clipMin;\n"
1559			"patch out highp vec3 vp_bbox_clipMax;\n";
1560
1561	if (m_calcPerPrimitiveBBox)
1562	{
1563		buf <<	"\n";
1564		if (m_hasGeometryStage)
1565			buf << genShaderFunction(SHADER_FUNC_MIRROR_X);
1566		buf << genShaderFunction(SHADER_FUNC_MIRROR_Y);
1567
1568		buf <<	"vec4 transformVec(in highp vec4 p)\n"
1569				"{\n"
1570				"	return " << ((m_hasGeometryStage) ? ("mirrorX(mirrorY(p))") : ("mirrorY(p)")) << ";\n"
1571				"}\n";
1572	}
1573
1574	buf <<	"\n"
1575			"void main()\n"
1576			"{\n"
1577			"	// convert to nonsensical coordinates, just in case\n"
1578			"	gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position.wzxy;\n"
1579			"	tess_ctrl_color[gl_InvocationID] = vtx_color[gl_InvocationID];\n"
1580			"\n"
1581			"	gl_TessLevelOuter[0] = 0.8; // will be rounded up to 1\n"
1582			"	gl_TessLevelOuter[1] = u_tessellationLevel;\n";
1583
1584	if (m_calcPerPrimitiveBBox)
1585	{
1586		buf <<	"\n"
1587				"	highp vec4 bboxMin = min(transformVec(gl_in[0].gl_Position),\n"
1588				"	                         transformVec(gl_in[1].gl_Position));\n"
1589				"	highp vec4 bboxMax = max(transformVec(gl_in[0].gl_Position),\n"
1590				"	                         transformVec(gl_in[1].gl_Position));\n";
1591	}
1592	else
1593	{
1594		buf <<	"\n"
1595				"	highp vec4 bboxMin = u_primitiveBBoxMin;\n"
1596				"	highp vec4 bboxMax = u_primitiveBBoxMax;\n";
1597	}
1598
1599	if (!m_useGlobalState)
1600		buf <<	"\n"
1601				"	gl_BoundingBoxEXT[0] = bboxMin;\n"
1602				"	gl_BoundingBoxEXT[1] = bboxMax;\n";
1603
1604	buf <<	"	vp_bbox_expansionSize = u_lineWidth;\n"
1605			"	vp_bbox_clipMin = min(vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMin.w,\n"
1606			"	                      vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMax.w);\n"
1607			"	vp_bbox_clipMax = max(vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMin.w,\n"
1608			"	                      vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMax.w);\n"
1609			"}\n";
1610
1611	return buf.str();
1612}
1613
1614std::string LineRenderCase::genTessellationEvaluationSource (void) const
1615{
1616	std::ostringstream	buf;
1617
1618	buf <<	"#version 310 es\n"
1619			"#extension GL_EXT_tessellation_shader : require\n"
1620			"layout(isolines) in;"
1621			"\n"
1622			"in highp vec4 tess_ctrl_color[];\n"
1623			"out highp vec4 tess_color;\n"
1624			"uniform highp vec4 u_posScale;\n"
1625			"\n"
1626			"patch in highp float vp_bbox_expansionSize;\n"
1627			"patch in highp vec3 vp_bbox_clipMin;\n"
1628			"patch in highp vec3 vp_bbox_clipMax;\n"
1629			"flat out highp float v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize;\n"
1630			"flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n"
1631			"flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n"
1632		<<	genShaderFunction(SHADER_FUNC_MIRROR_Y)
1633		<<	"void main()\n"
1634			"{\n"
1635			"	// non-trivial tessellation evaluation shader, convert from nonsensical coords, flip vertically\n"
1636			"	gl_Position = mirrorY(mix(gl_in[0].gl_Position.zwyx, gl_in[1].gl_Position.zwyx, gl_TessCoord.x));\n"
1637			"	tess_color = tess_ctrl_color[0];\n"
1638			"	v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize = vp_bbox_expansionSize;\n"
1639			"	v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin = vp_bbox_clipMin;\n"
1640			"	v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax = vp_bbox_clipMax;\n"
1641			"}\n";
1642
1643	return buf.str();
1644}
1645
1646std::string LineRenderCase::genGeometrySource (void) const
1647{
1648	const char* const	colorInputName = (m_hasTessellationStage) ? ("tess_color") : ("vtx_color");
1649	std::ostringstream	buf;
1650
1651	buf <<	"#version 310 es\n"
1652			"#extension GL_EXT_geometry_shader : require\n"
1653			"layout(lines) in;\n"
1654			"layout(max_vertices=5, line_strip) out;\n"
1655			"\n"
1656			"in highp vec4 " << colorInputName << "[2];\n"
1657			"out highp vec4 geo_color;\n"
1658			"uniform highp vec4 u_posScale;\n"
1659			"\n"
1660			"\n"
1661			"flat in highp float v_geo_bbox_expansionSize[2];\n"
1662			"flat in highp vec3 v_geo_bbox_clipMin[2];\n"
1663			"flat in highp vec3 v_geo_bbox_clipMax[2];\n"
1664			"flat out highp vec3 v_bbox_clipMin;\n"
1665			"flat out highp vec3 v_bbox_clipMax;\n"
1666			"flat out highp float v_bbox_expansionSize;\n"
1667		<<	genShaderFunction(SHADER_FUNC_MIRROR_X)
1668		<<	"\n"
1669			"void setVisualizationVaryings()\n"
1670			"{\n"
1671			"	v_bbox_expansionSize = v_geo_bbox_expansionSize[0];\n"
1672			"	v_bbox_clipMin = v_geo_bbox_clipMin[0];\n"
1673			"	v_bbox_clipMax = v_geo_bbox_clipMax[0];\n"
1674			"}\n"
1675			"void main()\n"
1676			"{\n"
1677			"	// Non-trivial geometry shader: 1-to-3 amplification, mirror horizontally\n"
1678			"	highp vec4 p0 = mirrorX(gl_in[0].gl_Position);\n"
1679			"	highp vec4 p1 = mirrorX(gl_in[1].gl_Position);\n"
1680			"	highp vec4 lineColor = " << colorInputName << "[0];\n"
1681			"\n"
1682			"	// output two separate primitives, just in case\n"
1683			"	gl_Position = mix(p0, p1, 0.00); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n"
1684			"	gl_Position = mix(p0, p1, 0.33); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n"
1685			"	EndPrimitive();\n"
1686			"\n"
1687			"	gl_Position = mix(p0, p1, 0.33); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n"
1688			"	gl_Position = mix(p0, p1, 0.67); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n"
1689			"	gl_Position = mix(p0, p1, 1.00); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n"
1690			"	EndPrimitive();\n"
1691			"}\n";
1692
1693	return buf.str();
1694}
1695
1696LineRenderCase::IterationConfig LineRenderCase::generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const
1697{
1698	const int numMaxAttempts = 128;
1699
1700	// Avoid too narrow viewports, lines could merge together. Require viewport is at least 2.5x the size of the line bodies.
1701	for (int attemptNdx = 0; attemptNdx < numMaxAttempts; ++attemptNdx)
1702	{
1703		const IterationConfig& config = generateRandomConfig((0xDEDEDEu * (deUint32)iteration) ^ (0xABAB13 * attemptNdx), renderTargetSize);
1704
1705		if ((float)config.viewportSize.x() * (config.patternSize.x() * 0.5f) > 2.5f * (float)m_patternSide * (float)m_wideLineLineWidth &&
1706			(float)config.viewportSize.y() * (config.patternSize.y() * 0.5f) > 2.5f * (float)m_patternSide * (float)m_wideLineLineWidth)
1707		{
1708			return config;
1709		}
1710	}
1711
1712	DE_ASSERT(false);
1713	return IterationConfig();
1714}
1715
1716void LineRenderCase::getAttributeData (std::vector<tcu::Vec4>& data) const
1717{
1718	const tcu::Vec4		green		(0.0f, 1.0f, 0.0f, 1.0f);
1719	const tcu::Vec4		blue		(0.0f, 0.0f, 1.0f, 1.0f);
1720	std::vector<int>	cellOrder	(m_patternSide * m_patternSide * 2);
1721	de::Random			rnd			(0xDE12345);
1722
1723	// generate crosshatch pattern with segments in random order
1724	for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
1725		cellOrder[ndx] = ndx;
1726	rnd.shuffle(cellOrder.begin(), cellOrder.end());
1727
1728	data.resize(cellOrder.size() * 4);
1729	for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
1730	{
1731		const int segmentID		= cellOrder[ndx];
1732		const int direction		= segmentID & 0x01;
1733		const int majorCoord	= (segmentID >> 1) / m_patternSide;
1734		const int minorCoord	= (segmentID >> 1) % m_patternSide;
1735
1736		if (direction)
1737		{
1738			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);
1739			data[(ndx * 2 + 0) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = green;
1740			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);
1741			data[(ndx * 2 + 1) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = green;
1742		}
1743		else
1744		{
1745			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);
1746			data[(ndx * 2 + 0) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = blue;
1747			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);
1748			data[(ndx * 2 + 1) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = blue;
1749		}
1750	}
1751}
1752
1753void LineRenderCase::renderTestPattern (const IterationConfig& config)
1754{
1755	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
1756
1757	setupRender(config);
1758
1759	if (m_hasTessellationStage)
1760	{
1761		const glw::GLint	tessLevelPos	= gl.getUniformLocation(m_program->getProgram(), "u_tessellationLevel");
1762		const glw::GLfloat	tessLevel		= 2.8f; // will be rounded up
1763
1764		TCU_CHECK(tessLevelPos != -1);
1765
1766		m_testCtx.getLog() << tcu::TestLog::Message << "u_tessellationLevel = " << tessLevel << tcu::TestLog::EndMessage;
1767
1768		gl.uniform1f(tessLevelPos, tessLevel);
1769		gl.patchParameteri(GL_PATCH_VERTICES, 2);
1770		GLU_EXPECT_NO_ERROR(gl.getError(), "patch param");
1771	}
1772
1773	if (m_isWideLineCase)
1774		gl.lineWidth((float)m_wideLineLineWidth);
1775
1776	gl.uniform1f(gl.getUniformLocation(m_program->getProgram(), "u_lineWidth"), (m_isWideLineCase) ? ((float)m_wideLineLineWidth) : (1.0f));
1777
1778	m_testCtx.getLog() << tcu::TestLog::Message << "Rendering pattern." << tcu::TestLog::EndMessage;
1779
1780	gl.enable(GL_BLEND);
1781	gl.blendFunc(GL_ONE, GL_ONE);
1782	gl.blendEquation(GL_FUNC_ADD);
1783
1784	gl.drawArrays((m_hasTessellationStage) ? (GL_PATCHES) : (GL_LINES), 0, m_patternSide * m_patternSide * 2 * 2);
1785	GLU_EXPECT_NO_ERROR(gl.getError(), "draw");
1786}
1787
1788void LineRenderCase::verifyRenderResult (const IterationConfig& config)
1789{
1790	const glw::Functions&	gl						= m_context.getRenderContext().getFunctions();
1791	const bool				isMsaa					= m_context.getRenderTarget().getNumSamples() > 1;
1792	const ProjectedBBox		projectedBBox			= projectBoundingBox(config.bbox);
1793	const float				lineWidth				= (m_isWideLineCase) ? ((float)m_wideLineLineWidth) : (1.0f);
1794	const tcu::IVec4		viewportBBoxArea		= getViewportBoundingBoxArea(projectedBBox, config.viewportSize, lineWidth);
1795	const tcu::IVec4		viewportPatternArea		= getViewportPatternArea(config.patternPos, config.patternSize, config.viewportSize, ROUND_INWARDS);
1796	const tcu::IVec2		expectedHorizontalLines	= getNumberOfLinesRange(viewportBBoxArea.y(), viewportBBoxArea.w(), config.patternPos.y(), config.patternSize.y(), config.viewportSize.y(), DIRECTION_VERTICAL);
1797	const tcu::IVec2		expectedVerticalLines	= getNumberOfLinesRange(viewportBBoxArea.x(), viewportBBoxArea.z(), config.patternPos.x(), config.patternSize.x(), config.viewportSize.x(), DIRECTION_HORIZONTAL);
1798	const tcu::IVec4		verificationArea		= tcu::IVec4(de::max(viewportBBoxArea.x(), 0),
1799																 de::max(viewportBBoxArea.y(), 0),
1800																 de::min(viewportBBoxArea.z(), config.viewportSize.x()),
1801																 de::min(viewportBBoxArea.w(), config.viewportSize.y()));
1802
1803	tcu::Surface			viewportSurface			(config.viewportSize.x(), config.viewportSize.y());
1804	bool					anyError				= false;
1805	bool					msaaRelaxationRequired	= false;
1806	int						messageLimitCounter		= 8;
1807
1808	if (!m_calcPerPrimitiveBBox)
1809		m_testCtx.getLog()
1810			<< tcu::TestLog::Message
1811			<< "Projected bounding box: (clip space)\n"
1812				<< "\tx: [" << projectedBBox.min.x() << "," << projectedBBox.max.x() << "]\n"
1813				<< "\ty: [" << projectedBBox.min.y() << "," << projectedBBox.max.y() << "]\n"
1814				<< "\tz: [" << projectedBBox.min.z() << "," << projectedBBox.max.z() << "]\n"
1815			<< "In viewport coordinates:\n"
1816				<< "\tx: [" << viewportBBoxArea.x() << ", " << viewportBBoxArea.z() << "]\n"
1817				<< "\ty: [" << viewportBBoxArea.y() << ", " << viewportBBoxArea.w() << "]\n"
1818			<< "Verifying render results within the bounding box:\n"
1819			<< tcu::TestLog::EndMessage;
1820	else
1821		m_testCtx.getLog()
1822			<< tcu::TestLog::Message
1823			<< "Verifying render result:"
1824			<< tcu::TestLog::EndMessage;
1825
1826	m_testCtx.getLog()
1827		<< tcu::TestLog::Message
1828			<< "\tCalculating number of horizontal and vertical lines within the bounding box, expecting:\n"
1829			<< "\t[" << expectedHorizontalLines.x() << ", " << expectedHorizontalLines.y() << "] horizontal lines.\n"
1830			<< "\t[" << expectedVerticalLines.x() << ", " << expectedVerticalLines.y() << "] vertical lines.\n"
1831		<< tcu::TestLog::EndMessage;
1832
1833	if (m_fbo)
1834		gl.bindFramebuffer(GL_READ_FRAMEBUFFER, **m_fbo);
1835	glu::readPixels(m_context.getRenderContext(), config.viewportPos.x(), config.viewportPos.y(), viewportSurface.getAccess());
1836
1837	// scan rows
1838	for (int y = de::max(verificationArea.y(), viewportPatternArea.y()); y < de::min(verificationArea.w(), viewportPatternArea.w()); ++y)
1839	{
1840		const deUint8 result = scanRow(viewportSurface.getAccess(),
1841									   y,
1842									   verificationArea.x(),
1843									   verificationArea.z(),
1844									   expectedVerticalLines,
1845									   messageLimitCounter);
1846
1847		if ((result & SCANRESULT_NUM_LINES_OK_BIT) == 0)
1848			anyError = true;
1849		else if ((result & SCANRESULT_LINE_WIDTH_OK_BIT) == 0)
1850		{
1851			if (m_isWideLineCase && isMsaa)
1852			{
1853				// multisampled wide lines might not be supported
1854				msaaRelaxationRequired = true;
1855			}
1856			else
1857				anyError = true;
1858		}
1859	}
1860
1861	// scan columns
1862	for (int x = de::max(verificationArea.x(), viewportPatternArea.x()); x < de::min(verificationArea.z(), viewportPatternArea.z()); ++x)
1863	{
1864		const deUint8 result = scanColumn(viewportSurface.getAccess(),
1865										  x,
1866										  verificationArea.y(),
1867										  verificationArea.w(),
1868										  expectedHorizontalLines,
1869										  messageLimitCounter);
1870
1871		if ((result & SCANRESULT_NUM_LINES_OK_BIT) == 0)
1872			anyError = true;
1873		else if ((result & SCANRESULT_LINE_WIDTH_OK_BIT) == 0)
1874		{
1875			if (m_isWideLineCase && isMsaa)
1876			{
1877				// multisampled wide lines might not be supported
1878				msaaRelaxationRequired = true;
1879			}
1880			else
1881				anyError = true;
1882		}
1883	}
1884
1885	if (anyError || msaaRelaxationRequired)
1886	{
1887		if (messageLimitCounter < 0)
1888			m_testCtx.getLog() << tcu::TestLog::Message << "Omitted " << (-messageLimitCounter) << " row/column error descriptions." << tcu::TestLog::EndMessage;
1889
1890		m_testCtx.getLog()
1891			<< tcu::TestLog::Message
1892			<< "Image verification failed."
1893			<< tcu::TestLog::EndMessage
1894			<< tcu::TestLog::ImageSet("Images", "Image verification")
1895			<< tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess())
1896			<< tcu::TestLog::EndImageSet;
1897
1898		if (anyError)
1899			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
1900		else
1901		{
1902			// MSAA wide lines are optional
1903			m_testCtx.setTestResult(QP_TEST_RESULT_COMPATIBILITY_WARNING, "Multisampled wide line verification failed");
1904		}
1905	}
1906	else
1907	{
1908		m_testCtx.getLog()
1909			<< tcu::TestLog::Message
1910			<< "Result image ok."
1911			<< tcu::TestLog::EndMessage
1912			<< tcu::TestLog::ImageSet("Images", "Image verification")
1913			<< tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess())
1914			<< tcu::TestLog::EndImageSet;
1915	}
1916}
1917
1918tcu::IVec2 LineRenderCase::getNumberOfLinesRange (int queryAreaBegin, int queryAreaEnd, float patternStart, float patternSize, int viewportArea, QueryDirection queryDir) const
1919{
1920	// pattern is not symmetric due to mirroring
1921	const int	patternStartNdx	= (queryDir == DIRECTION_HORIZONTAL) ? ((m_hasGeometryStage) ? (1) : (0)) : ((m_hasTessellationStage) ? (1) : (0));
1922	const int	patternEndNdx	= patternStartNdx + m_patternSide;
1923
1924	int			numLinesMin		= 0;
1925	int			numLinesMax		= 0;
1926
1927	for (int lineNdx = patternStartNdx; lineNdx < patternEndNdx; ++lineNdx)
1928	{
1929		const float linePos		= (patternStart + (float(lineNdx) / float(m_patternSide)) * patternSize) * 0.5f + 0.5f;
1930		const float lineWidth	= (m_isWideLineCase) ? ((float)m_wideLineLineWidth) : (1.0f);
1931
1932		if (linePos * (float)viewportArea > (float)queryAreaBegin + 1.0f &&
1933			linePos * (float)viewportArea < (float)queryAreaEnd   - 1.0f)
1934		{
1935			// line center is within the area
1936			++numLinesMin;
1937			++numLinesMax;
1938		}
1939		else if (linePos * (float)viewportArea > (float)queryAreaBegin - lineWidth*0.5f - 1.0f &&
1940		         linePos * (float)viewportArea < (float)queryAreaEnd   + lineWidth*0.5f + 1.0f)
1941		{
1942			// line could leak into area
1943			++numLinesMax;
1944		}
1945	}
1946
1947	return tcu::IVec2(numLinesMin, numLinesMax);
1948}
1949
1950deUint8 LineRenderCase::scanRow (const tcu::ConstPixelBufferAccess& access, int row, int rowBegin, int rowEnd, const tcu::IVec2& numLines, int& messageLimitCounter) const
1951{
1952	const bool numLinesOk	= checkAreaNumLines(access, tcu::IVec4(rowBegin, row, rowEnd - rowBegin, 1), messageLimitCounter, SCAN_ROW_COMPONENT_NDX, numLines);
1953	const bool lineWidthOk	= checkLineWidths(access, tcu::IVec2(rowBegin, row), tcu::IVec2(rowEnd, row), SCAN_ROW_COMPONENT_NDX, messageLimitCounter);
1954
1955	return	(deUint8)((numLinesOk	? (deUint8)SCANRESULT_NUM_LINES_OK_BIT	: 0u) |
1956					  (lineWidthOk	? (deUint8)SCANRESULT_LINE_WIDTH_OK_BIT	: 0u));
1957}
1958
1959deUint8 LineRenderCase::scanColumn (const tcu::ConstPixelBufferAccess& access, int column, int columnBegin, int columnEnd, const tcu::IVec2& numLines, int& messageLimitCounter) const
1960{
1961	const bool numLinesOk	= checkAreaNumLines(access, tcu::IVec4(column, columnBegin, 1, columnEnd - columnBegin), messageLimitCounter, SCAN_COL_COMPONENT_NDX, numLines);
1962	const bool lineWidthOk	= checkLineWidths(access, tcu::IVec2(column, columnBegin), tcu::IVec2(column, columnEnd), SCAN_COL_COMPONENT_NDX, messageLimitCounter);
1963
1964	return	(deUint8)((numLinesOk	? (deUint8)SCANRESULT_NUM_LINES_OK_BIT	: 0u) |
1965					  (lineWidthOk	? (deUint8)SCANRESULT_LINE_WIDTH_OK_BIT	: 0u));
1966}
1967
1968bool LineRenderCase::checkAreaNumLines (const tcu::ConstPixelBufferAccess& access, const tcu::IVec4& area, int& messageLimitCounter, int componentNdx, const tcu::IVec2& numLines) const
1969{
1970	// Num maxima == num lines
1971	const tcu::ConstPixelBufferAccess	subAccess		= tcu::getSubregion(access, area.x(), area.y(), 0, area.z(), area.w(), 1);
1972	const tcu::IVec2					numMinimaMaxima	= getNumMinimaMaxima(subAccess, componentNdx);
1973	const int							numMaxima		= numMinimaMaxima.y();
1974
1975	// In valid range
1976	if (numMaxima >= numLines.x() && numMaxima <= numLines.y())
1977		return true;
1978
1979	if (--messageLimitCounter < 0)
1980		return false;
1981
1982	if (area.z() == 1)
1983		m_testCtx.getLog()
1984			<< tcu::TestLog::Message
1985			<< "On column " << area.x() << ", y: [" << area.y() << "," << (area.y()+area.w()) << "):\n"
1986				<< "\tExpected [" << numLines.x() << ", " << numLines.y() << "] lines but the number of lines in the area is " << numMaxima
1987			<< tcu::TestLog::EndMessage;
1988	else
1989		m_testCtx.getLog()
1990			<< tcu::TestLog::Message
1991			<< "On row " << area.y() << ", x: [" << area.x() << "," << (area.x()+area.z()) << "):\n"
1992				<< "\tExpected [" << numLines.x() << ", " << numLines.y() << "] lines but the number of lines in the area is " << numMaxima
1993			<< tcu::TestLog::EndMessage;
1994
1995	return false;
1996}
1997
1998tcu::IVec2 LineRenderCase::getNumMinimaMaxima (const tcu::ConstPixelBufferAccess& access, int componentNdx) const
1999{
2000	DE_ASSERT(access.getWidth() == 1 || access.getHeight() == 1);
2001
2002	int previousValue	= -1;
2003	int previousSign	= 0;
2004	int numMinima		= 0;
2005	int numMaxima		= 0;
2006
2007	for (int y = 0; y < access.getHeight(); ++y)
2008	for (int x = 0; x < access.getWidth(); ++x)
2009	{
2010		const int componentValue = access.getPixelInt(x, y)[componentNdx];
2011
2012		if (previousValue != -1)
2013		{
2014			const int sign = (componentValue > previousValue) ? (+1) : (componentValue < previousValue) ? (-1) : (0);
2015
2016			// local minima/maxima in sign changes (zero signless)
2017			if (sign != 0 && sign == -previousSign)
2018			{
2019				previousSign = sign;
2020
2021				if (sign > 0)
2022					++numMinima;
2023				else
2024					++numMaxima;
2025			}
2026			else if (sign != 0 && previousSign == 0)
2027			{
2028				previousSign = sign;
2029
2030				// local extreme at the start boundary
2031				if (sign > 0)
2032					++numMinima;
2033				else
2034					++numMaxima;
2035			}
2036		}
2037
2038		previousValue = componentValue;
2039	}
2040
2041	// local extreme at the end boundary
2042	if (previousSign > 0)
2043		++numMaxima;
2044	else if (previousSign < 0)
2045		++numMinima;
2046	else
2047	{
2048		++numMaxima;
2049		++numMinima;
2050	}
2051
2052	return tcu::IVec2(numMinima, numMaxima);
2053}
2054
2055bool LineRenderCase::checkLineWidths (const tcu::ConstPixelBufferAccess& access, const tcu::IVec2& begin, const tcu::IVec2& end, int componentNdx, int& messageLimitCounter) const
2056{
2057	const bool			multisample		= m_context.getRenderTarget().getNumSamples() > 1;
2058	const int			lineRenderWidth	= (m_isWideLineCase) ? (m_wideLineLineWidth) : 1;
2059	const tcu::IVec2	lineWidthRange	= (multisample)
2060											? (tcu::IVec2(lineRenderWidth, lineRenderWidth+1))	// multisampled "smooth" lines may spread to neighboring pixel
2061											: (tcu::IVec2(lineRenderWidth, lineRenderWidth));
2062
2063	int					lineWidth		= 0;
2064	bool				bboxLimitedLine	= false;
2065	bool				anyError		= false;
2066
2067	const tcu::IVec2	advance			= (begin.x() == end.x()) ? (tcu::IVec2(0, 1)) : (tcu::IVec2(1, 0));
2068
2069	// fragments before begin?
2070	if (access.getPixelInt(begin.x(), begin.y())[componentNdx] != 0)
2071	{
2072		bboxLimitedLine = true;
2073
2074		for (tcu::IVec2 cursor = begin - advance;; cursor -= advance)
2075		{
2076			if (cursor.x() < 0 || cursor.y() < 0)
2077			{
2078				break;
2079			}
2080			else if (access.getPixelInt(cursor.x(), cursor.y())[componentNdx] != 0)
2081			{
2082				++lineWidth;
2083			}
2084			else
2085				break;
2086		}
2087	}
2088
2089	for (tcu::IVec2 cursor = begin; cursor != end; cursor += advance)
2090	{
2091		const bool hit = (access.getPixelInt(cursor.x(), cursor.y())[componentNdx] != 0);
2092
2093		if (hit)
2094			++lineWidth;
2095		else if (lineWidth)
2096		{
2097			// Line is allowed to be be thinner if it borders the bbox boundary (since part of the line might have been discarded).
2098			const bool incorrectLineWidth = (lineWidth < lineWidthRange.x() && !bboxLimitedLine) || (lineWidth > lineWidthRange.y());
2099
2100			if (incorrectLineWidth)
2101			{
2102				anyError = true;
2103				printLineWidthError(cursor, lineWidth, lineWidthRange, advance.x() == 0, messageLimitCounter);
2104			}
2105
2106			lineWidth = 0;
2107			bboxLimitedLine = false;
2108		}
2109	}
2110
2111	// fragments after end?
2112	if (lineWidth)
2113	{
2114		for (tcu::IVec2 cursor = end;; cursor += advance)
2115		{
2116			if (cursor.x() >= access.getWidth() || cursor.y() >= access.getHeight())
2117			{
2118				if (lineWidth > lineWidthRange.y())
2119				{
2120					anyError = true;
2121					printLineWidthError(cursor, lineWidth, lineWidthRange, advance.x() == 0, messageLimitCounter);
2122				}
2123
2124				break;
2125			}
2126			else if (access.getPixelInt(cursor.x(), cursor.y())[componentNdx] != 0)
2127			{
2128				++lineWidth;
2129			}
2130			else if (lineWidth)
2131			{
2132				// only check that line width is not larger than expected. Line width may be smaller
2133				// since the scanning 'cursor' is now outside the bounding box.
2134				const bool incorrectLineWidth = (lineWidth > lineWidthRange.y());
2135
2136				if (incorrectLineWidth)
2137				{
2138					anyError = true;
2139					printLineWidthError(cursor, lineWidth, lineWidthRange, advance.x() == 0, messageLimitCounter);
2140				}
2141
2142				lineWidth = 0;
2143			}
2144		}
2145	}
2146
2147	return !anyError;
2148}
2149
2150void LineRenderCase::printLineWidthError (const tcu::IVec2& pos, int detectedLineWidth, const tcu::IVec2& lineWidthRange, bool isHorizontal, int& messageLimitCounter) const
2151{
2152	if (--messageLimitCounter < 0)
2153		return;
2154
2155	m_testCtx.getLog()
2156		<< tcu::TestLog::Message
2157		<< "Found incorrect line width near " << pos << ": (" << ((isHorizontal) ? ("horizontal") : ("vertical")) << " line)\n"
2158			<< "\tExpected line width in range [" << lineWidthRange.x() << ", " << lineWidthRange.y() << "] but found " << detectedLineWidth
2159		<< tcu::TestLog::EndMessage;
2160}
2161
2162class PointRenderCase : public BBoxRenderCase
2163{
2164public:
2165	enum
2166	{
2167		POINTFLAG_WIDE = 1u << FLAGBIT_USER_BIT,	//!< use wide points
2168	};
2169	struct GeneratedPoint
2170	{
2171		tcu::Vec2	center;
2172		int			size;
2173		bool		even;
2174	};
2175
2176							PointRenderCase					(Context& context, const char* name, const char* description, deUint32 flags);
2177							~PointRenderCase				(void);
2178
2179private:
2180	enum ResultPointType
2181	{
2182		POINT_FULL = 0,
2183		POINT_PARTIAL
2184	};
2185
2186	void					init							(void);
2187	void					deinit							(void);
2188
2189	std::string				genVertexSource					(void) const;
2190	std::string				genFragmentSource				(void) const;
2191	std::string				genTessellationControlSource	(void) const;
2192	std::string				genTessellationEvaluationSource	(void) const;
2193	std::string				genGeometrySource				(void) const;
2194
2195	IterationConfig			generateConfig					(int iteration, const tcu::IVec2& renderTargetSize) const;
2196	void					generateAttributeData			(void);
2197	void					getAttributeData				(std::vector<tcu::Vec4>& data) const;
2198	void					renderTestPattern				(const IterationConfig& config);
2199	void					verifyRenderResult				(const IterationConfig& config);
2200
2201	void					genReferencePointData			(const IterationConfig& config, std::vector<GeneratedPoint>& data) const;
2202	bool					verifyNarrowPointPattern		(const tcu::Surface& viewport, const std::vector<GeneratedPoint>& refPoints, const ProjectedBBox& bbox, int& logFloodCounter);
2203	bool					verifyWidePointPattern			(const tcu::Surface& viewport, const std::vector<GeneratedPoint>& refPoints, const ProjectedBBox& bbox, int& logFloodCounter);
2204	bool					verifyWidePoint					(const tcu::Surface& viewport, const GeneratedPoint& refPoint, const ProjectedBBox& bbox, ResultPointType pointType, int& logFloodCounter);
2205	bool					verifyWidePointAt				(const tcu::IVec2& pointPos, const tcu::Surface& viewport, const GeneratedPoint& refPoint, const tcu::IVec4& bbox, ResultPointType pointType, int componentNdx, int& logFloodCounter);
2206	tcu::IVec2				scanPointWidthAt				(const tcu::IVec2& pointPos, const tcu::Surface& viewport, int expectedPointSize, int componentNdx) const;
2207
2208	const int				m_numStripes;
2209	const bool				m_isWidePointCase;
2210	std::vector<tcu::Vec4>	m_attribData;
2211};
2212
2213PointRenderCase::PointRenderCase (Context& context, const char* name, const char* description, deUint32 flags)
2214	: BBoxRenderCase	(context, name, description, 12, flags)
2215	, m_numStripes		(4)
2216	, m_isWidePointCase	((flags & POINTFLAG_WIDE) != 0)
2217{
2218}
2219
2220PointRenderCase::~PointRenderCase (void)
2221{
2222}
2223
2224void PointRenderCase::init (void)
2225{
2226	if (m_isWidePointCase)
2227	{
2228		// extensions
2229		if (m_hasGeometryStage && !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_point_size"))
2230			throw tcu::NotSupportedError("Test requires GL_EXT_geometry_point_size extension");
2231		if (m_hasTessellationStage && !m_hasGeometryStage && !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_point_size"))
2232			throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_point_size extension");
2233
2234		// point size range
2235		{
2236			glw::GLfloat pointSizeRange[2] = {0.0f, 0.0f};
2237			m_context.getRenderContext().getFunctions().getFloatv(GL_ALIASED_POINT_SIZE_RANGE, pointSizeRange);
2238
2239			if (pointSizeRange[1] < 5.0f)
2240				throw tcu::NotSupportedError("Test requires point size 5.0");
2241		}
2242	}
2243
2244	m_testCtx.getLog()
2245		<< tcu::TestLog::Message
2246		<< "Rendering point pattern to " << ((m_renderTarget == RENDERTARGET_DEFAULT) ? ("default frame buffer") : ("fbo")) << ".\n"
2247		<< "Half of the points are green, half blue. Using additive blending.\n"
2248		<< "Points are in random order, varying pattern size and location for each iteration.\n"
2249		<< "Marking all discardable fragments (fragments outside the bounding box) with a fully saturated red channel."
2250		<< tcu::TestLog::EndMessage;
2251
2252	generateAttributeData();
2253
2254	BBoxRenderCase::init();
2255}
2256
2257void PointRenderCase::deinit (void)
2258{
2259	// clear data
2260	m_attribData = std::vector<tcu::Vec4>();
2261
2262	// deinit parent
2263	BBoxRenderCase::deinit();
2264}
2265
2266std::string PointRenderCase::genVertexSource (void) const
2267{
2268	std::ostringstream	buf;
2269
2270	buf <<	"#version 310 es\n"
2271			"in highp vec4 a_position;\n"
2272			"in highp vec4 a_color;\n"
2273			"out highp vec4 vtx_color;\n"
2274			"uniform highp vec4 u_posScale;\n"
2275			"\n";
2276	if (!m_hasTessellationStage)
2277	{
2278		DE_ASSERT(m_useGlobalState);
2279		buf <<	"uniform highp vec4 u_primitiveBBoxMin;\n"
2280				"uniform highp vec4 u_primitiveBBoxMax;\n"
2281				"\n"
2282				"flat out highp float v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize;\n"
2283				"flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n"
2284				"flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n"
2285				"\n";
2286	}
2287
2288	buf <<	"void main()\n"
2289			"{\n"
2290			"	highp vec2 patternOffset = u_posScale.xy;\n"
2291			"	highp vec2 patternScale = u_posScale.zw;\n"
2292			"	highp float pointSize = "
2293					<< ((m_isWidePointCase && !m_hasTessellationStage && !m_hasGeometryStage) ? ("(a_color.g > 0.0) ? (5.0) : (3.0)") : ("1.0"))
2294					<< ";\n"
2295		<<	"	gl_Position = vec4(a_position.xy * patternScale + patternOffset, a_position.z, a_position.w);\n"
2296			"	gl_PointSize = pointSize;\n"
2297			"	vtx_color = a_color;\n";
2298
2299	if (!m_hasTessellationStage)
2300	{
2301		DE_ASSERT(m_useGlobalState);
2302		buf <<	"\n"
2303				"	v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize = pointSize;\n"
2304				"	v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin =\n"
2305				"	    min(vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMin.w,\n"
2306				"	        vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMax.w);\n"
2307				"	v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax =\n"
2308				"	    min(vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMin.w,\n"
2309				"	        vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMax.w);\n";
2310	}
2311
2312	buf <<	"}\n";
2313	return buf.str();
2314}
2315
2316std::string PointRenderCase::genFragmentSource (void) const
2317{
2318	const char* const	colorInputName = (m_hasGeometryStage) ? ("geo_color") : (m_hasTessellationStage) ? ("tess_color") : ("vtx_color");
2319	std::ostringstream	buf;
2320
2321	buf <<	"#version 310 es\n"
2322			"in mediump vec4 " << colorInputName << ";\n"
2323			"layout(location = 0) out mediump vec4 o_color;\n"
2324		<<	genShaderFunction(SHADER_FUNC_INSIDE_BBOX)
2325		<<	"\n"
2326			"void main()\n"
2327			"{\n"
2328			"	mediump vec4 baseColor = " << colorInputName << ";\n"
2329			"	mediump float redChannel;\n"
2330			"	if (fragmentInsideTheBBox(gl_FragCoord.z))\n"
2331			"		redChannel = 0.0;\n"
2332			"	else\n"
2333			"		redChannel = 1.0;\n"
2334			"	o_color = vec4(redChannel, baseColor.g, baseColor.b, baseColor.a);\n"
2335			"}\n";
2336
2337	return buf.str();
2338}
2339
2340std::string PointRenderCase::genTessellationControlSource (void) const
2341{
2342	const bool			tessellationWidePoints = (m_isWidePointCase) && (!m_hasGeometryStage);
2343	std::ostringstream	buf;
2344
2345	buf <<	"#version 310 es\n"
2346			"#extension GL_EXT_tessellation_shader : require\n"
2347			"#extension GL_EXT_primitive_bounding_box : require\n"
2348		<<	((tessellationWidePoints) ? ("#extension GL_EXT_tessellation_point_size : require\n") : (""))
2349		<<	"layout(vertices=1) out;"
2350			"\n"
2351			"in highp vec4 vtx_color[];\n"
2352			"out highp vec4 tess_ctrl_color[];\n"
2353			"uniform highp float u_tessellationLevel;\n"
2354			"uniform highp vec4 u_posScale;\n";
2355
2356	if (!m_calcPerPrimitiveBBox)
2357	{
2358		buf <<	"uniform highp vec4 u_primitiveBBoxMin;\n"
2359				"uniform highp vec4 u_primitiveBBoxMax;\n";
2360	}
2361
2362	buf <<	"patch out highp vec3 vp_bbox_clipMin;\n"
2363			"patch out highp vec3 vp_bbox_clipMax;\n";
2364
2365	if (m_calcPerPrimitiveBBox)
2366	{
2367		buf <<	"\n";
2368		if (m_hasGeometryStage)
2369			buf << genShaderFunction(SHADER_FUNC_MIRROR_X);
2370		buf << genShaderFunction(SHADER_FUNC_MIRROR_Y);
2371
2372		buf <<	"vec4 transformVec(in highp vec4 p)\n"
2373				"{\n"
2374				"	return " << ((m_hasGeometryStage) ? ("mirrorX(mirrorY(p))") : ("mirrorY(p)")) << ";\n"
2375				"}\n";
2376	}
2377
2378	buf <<	"\n"
2379			"void main()\n"
2380			"{\n"
2381			"	// convert to nonsensical coordinates, just in case\n"
2382			"	gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position.wzxy;\n"
2383			"	tess_ctrl_color[gl_InvocationID] = vtx_color[gl_InvocationID];\n"
2384			"\n"
2385			"	gl_TessLevelOuter[0] = u_tessellationLevel;\n"
2386			"	gl_TessLevelOuter[1] = u_tessellationLevel;\n"
2387			"	gl_TessLevelOuter[2] = u_tessellationLevel;\n"
2388			"	gl_TessLevelOuter[3] = u_tessellationLevel;\n"
2389			"	gl_TessLevelInner[0] = 0.8; // will be rounded up to 1\n"
2390			"	gl_TessLevelInner[1] = 0.8; // will be rounded up to 1\n";
2391
2392	if (m_calcPerPrimitiveBBox)
2393	{
2394		buf <<	"\n";
2395
2396		if (m_hasGeometryStage)
2397			buf <<	"	const vec2 minExpansion = vec2(0.07 + 0.05, 0.07 + 0.02); // eval and geometry shader\n"
2398					"	const vec2 maxExpansion = vec2(0.07 + 0.05, 0.07 + 0.03); // eval and geometry shader\n";
2399		else
2400			buf <<	"	const vec2 minExpansion = vec2(0.07, 0.07); // eval shader\n"
2401					"	const vec2 maxExpansion = vec2(0.07, 0.07); // eval shader\n";
2402
2403		buf <<	"	highp vec2 patternScale = u_posScale.zw;\n"
2404				"	highp vec4 bboxMin = transformVec(gl_in[0].gl_Position) - vec4(minExpansion * patternScale, 0.0, 0.0);\n"
2405				"	highp vec4 bboxMax = transformVec(gl_in[0].gl_Position) + vec4(maxExpansion * patternScale, 0.0, 0.0);\n";
2406	}
2407	else
2408	{
2409		buf <<	"\n"
2410				"	highp vec4 bboxMin = u_primitiveBBoxMin;\n"
2411				"	highp vec4 bboxMax = u_primitiveBBoxMax;\n";
2412	}
2413	if (!m_useGlobalState)
2414		buf <<	"\n"
2415				"	gl_BoundingBoxEXT[0] = bboxMin;\n"
2416				"	gl_BoundingBoxEXT[1] = bboxMax;\n";
2417
2418	buf <<	"	vp_bbox_clipMin = min(vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMin.w,\n"
2419			"	                      vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMax.w);\n"
2420			"	vp_bbox_clipMax = max(vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMin.w,\n"
2421			"	                      vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMax.w);\n"
2422			"}\n";
2423
2424	return buf.str();
2425}
2426
2427std::string PointRenderCase::genTessellationEvaluationSource (void) const
2428{
2429	const bool			tessellationWidePoints = (m_isWidePointCase) && (!m_hasGeometryStage);
2430	std::ostringstream	buf;
2431
2432	buf <<	"#version 310 es\n"
2433			"#extension GL_EXT_tessellation_shader : require\n"
2434		<<	((tessellationWidePoints) ? ("#extension GL_EXT_tessellation_point_size : require\n") : (""))
2435		<<	"layout(quads, point_mode) in;"
2436			"\n"
2437			"in highp vec4 tess_ctrl_color[];\n"
2438			"out highp vec4 tess_color;\n"
2439			"uniform highp vec4 u_posScale;\n"
2440			"\n"
2441			"patch in highp vec3 vp_bbox_clipMin;\n"
2442			"patch in highp vec3 vp_bbox_clipMax;\n"
2443		<<	((!m_hasGeometryStage) ? ("flat out highp float v_bbox_expansionSize;\n") : (""))
2444		<<	"flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n"
2445			"flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n"
2446			"\n"
2447		<<	genShaderFunction(SHADER_FUNC_MIRROR_Y)
2448		<<	"void main()\n"
2449			"{\n"
2450			"	// non-trivial tessellation evaluation shader, convert from nonsensical coords, flip vertically\n"
2451			"	highp vec2 patternScale = u_posScale.zw;\n"
2452			"	highp vec4 offset = vec4((gl_TessCoord.xy * 2.0 - vec2(1.0)) * 0.07 * patternScale, 0.0, 0.0);\n"
2453			"	highp float pointSize = " << ((tessellationWidePoints) ? ("(tess_ctrl_color[0].g > 0.0) ? (5.0) : (3.0)") : ("1.0")) << ";\n"
2454			"	gl_Position = mirrorY(gl_in[0].gl_Position.zwyx + offset);\n";
2455
2456	if (tessellationWidePoints)
2457		buf << "	gl_PointSize = pointSize;\n";
2458
2459	buf <<	"	tess_color = tess_ctrl_color[0];\n"
2460		<<	((!m_hasGeometryStage) ? ("v_bbox_expansionSize = pointSize;\n") : (""))
2461		<<	"	v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin = vp_bbox_clipMin;\n"
2462			"	v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax = vp_bbox_clipMax;\n"
2463			"}\n";
2464
2465	return buf.str();
2466}
2467
2468std::string PointRenderCase::genGeometrySource (void) const
2469{
2470	const char* const	colorInputName = (m_hasTessellationStage) ? ("tess_color") : ("vtx_color");
2471	std::ostringstream	buf;
2472
2473	buf <<	"#version 310 es\n"
2474			"#extension GL_EXT_geometry_shader : require\n"
2475		<<	((m_isWidePointCase) ? ("#extension GL_EXT_geometry_point_size : require\n") : (""))
2476		<<	"layout(points) in;\n"
2477			"layout(max_vertices=3, points) out;\n"
2478			"\n"
2479			"in highp vec4 " << colorInputName << "[1];\n"
2480			"out highp vec4 geo_color;\n"
2481			"uniform highp vec4 u_posScale;\n"
2482			"\n"
2483			"flat in highp vec3 v_geo_bbox_clipMin[1];\n"
2484			"flat in highp vec3 v_geo_bbox_clipMax[1];\n"
2485			"flat out highp vec3 v_bbox_clipMin;\n"
2486			"flat out highp vec3 v_bbox_clipMax;\n"
2487			"flat out highp float v_bbox_expansionSize;\n"
2488			"\n"
2489		<<	genShaderFunction(SHADER_FUNC_MIRROR_X)
2490		<<	"\n"
2491			"void main()\n"
2492			"{\n"
2493			"	// Non-trivial geometry shader: 1-to-3 amplification, mirror horizontally\n"
2494			"	highp vec4 p0 = mirrorX(gl_in[0].gl_Position);\n"
2495			"	highp vec4 pointColor = " << colorInputName << "[0];\n"
2496			"	highp vec2 patternScale = u_posScale.zw;\n"
2497			"	highp float pointSize = "
2498				<< (m_isWidePointCase ? ("(pointColor.g > 0.0) ? (5.0) : (3.0)") : ("1.0"))
2499				<< ";\n"
2500			"\n"
2501			"	highp vec4 offsets[3] =\n"
2502			"		vec4[3](\n"
2503			"			vec4( 0.05 * patternScale.x, 0.03 * patternScale.y, 0.0, 0.0),\n"
2504			"			vec4(-0.01 * patternScale.x,-0.02 * patternScale.y, 0.0, 0.0),\n"
2505			"			vec4(-0.05 * patternScale.x, 0.02 * patternScale.y, 0.0, 0.0)\n"
2506			"		);\n"
2507			"	for (int ndx = 0; ndx < 3; ++ndx)\n"
2508			"	{\n"
2509			"		gl_Position = p0 + offsets[ndx];\n";
2510
2511	if (m_isWidePointCase)
2512		buf <<	"		gl_PointSize = pointSize;\n";
2513
2514	buf <<	"		v_bbox_clipMin = v_geo_bbox_clipMin[0];\n"
2515			"		v_bbox_clipMax = v_geo_bbox_clipMax[0];\n"
2516			"		v_bbox_expansionSize = pointSize;\n"
2517			"		geo_color = pointColor;\n"
2518			"		EmitVertex();\n"
2519			"	}\n"
2520			"}\n";
2521
2522	return buf.str();
2523}
2524
2525PointRenderCase::IterationConfig PointRenderCase::generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const
2526{
2527	IterationConfig config = generateRandomConfig(0xDEDEDEu * (deUint32)iteration, renderTargetSize);
2528
2529	// equal or larger -> expand according to shader expansion
2530	if (m_bboxSize == BBOXSIZE_EQUAL || m_bboxSize == BBOXSIZE_LARGER)
2531	{
2532		const tcu::Vec2 patternScale = config.patternSize;
2533
2534		if (m_hasTessellationStage)
2535		{
2536			config.bbox.min -= tcu::Vec4(0.07f * patternScale.x(), 0.07f * patternScale.y(), 0.0f, 0.0f);
2537			config.bbox.max += tcu::Vec4(0.07f * patternScale.x(), 0.07f * patternScale.y(), 0.0f, 0.0f);
2538		}
2539		if (m_hasGeometryStage)
2540		{
2541			config.bbox.min -= tcu::Vec4(0.05f * patternScale.x(), 0.02f * patternScale.y(), 0.0f, 0.0f);
2542			config.bbox.max += tcu::Vec4(0.05f * patternScale.x(), 0.03f * patternScale.y(), 0.0f, 0.0f);
2543		}
2544	}
2545
2546	return config;
2547}
2548
2549void PointRenderCase::generateAttributeData (void)
2550{
2551	const tcu::Vec4		green		(0.0f, 1.0f, 0.0f, 1.0f);
2552	const tcu::Vec4		blue		(0.0f, 0.0f, 1.0f, 1.0f);
2553	std::vector<int>	cellOrder	(m_numStripes * m_numStripes * 2);
2554	de::Random			rnd			(0xDE22446);
2555
2556	for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
2557		cellOrder[ndx] = ndx;
2558	rnd.shuffle(cellOrder.begin(), cellOrder.end());
2559
2560	m_attribData.resize(cellOrder.size() * 2);
2561	for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
2562	{
2563		const int pointID		= cellOrder[ndx];
2564		const int direction		= pointID & 0x01;
2565		const int majorCoord	= (pointID >> 1) / m_numStripes;
2566		const int minorCoord	= (pointID >> 1) % m_numStripes;
2567
2568		if (direction)
2569		{
2570			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);
2571			m_attribData[ndx * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = green;
2572		}
2573		else
2574		{
2575			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);
2576			m_attribData[ndx * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = blue;
2577		}
2578	}
2579}
2580
2581void PointRenderCase::getAttributeData (std::vector<tcu::Vec4>& data) const
2582{
2583	data = m_attribData;
2584}
2585
2586void PointRenderCase::renderTestPattern (const IterationConfig& config)
2587{
2588	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
2589
2590	setupRender(config);
2591
2592	if (m_hasTessellationStage)
2593	{
2594		const glw::GLint	tessLevelPos	= gl.getUniformLocation(m_program->getProgram(), "u_tessellationLevel");
2595		const glw::GLfloat	tessLevel		= 0.8f; // will be rounded up
2596
2597		TCU_CHECK(tessLevelPos != -1);
2598
2599		m_testCtx.getLog() << tcu::TestLog::Message << "u_tessellationLevel = " << tessLevel << tcu::TestLog::EndMessage;
2600
2601		gl.uniform1f(tessLevelPos, tessLevel);
2602		gl.patchParameteri(GL_PATCH_VERTICES, 1);
2603		GLU_EXPECT_NO_ERROR(gl.getError(), "patch param");
2604	}
2605
2606	m_testCtx.getLog() << tcu::TestLog::Message << "Rendering pattern." << tcu::TestLog::EndMessage;
2607
2608	gl.enable(GL_BLEND);
2609	gl.blendFunc(GL_ONE, GL_ONE);
2610	gl.blendEquation(GL_FUNC_ADD);
2611
2612	gl.drawArrays((m_hasTessellationStage) ? (GL_PATCHES) : (GL_POINTS), 0, m_numStripes * m_numStripes * 2);
2613	GLU_EXPECT_NO_ERROR(gl.getError(), "draw");
2614}
2615
2616void PointRenderCase::verifyRenderResult (const IterationConfig& config)
2617{
2618	const glw::Functions&		gl						= m_context.getRenderContext().getFunctions();
2619	const ProjectedBBox			projectedBBox			= projectBoundingBox(config.bbox);
2620	const tcu::IVec4			viewportBBoxArea		= getViewportBoundingBoxArea(projectedBBox, config.viewportSize);
2621
2622	tcu::Surface				viewportSurface			(config.viewportSize.x(), config.viewportSize.y());
2623	int							logFloodCounter			= 8;
2624	bool						anyError;
2625	std::vector<GeneratedPoint>	refPoints;
2626
2627	if (!m_calcPerPrimitiveBBox)
2628		m_testCtx.getLog()
2629			<< tcu::TestLog::Message
2630			<< "Projected bounding box: (clip space)\n"
2631				<< "\tx: [" << projectedBBox.min.x() << "," << projectedBBox.max.x() << "]\n"
2632				<< "\ty: [" << projectedBBox.min.y() << "," << projectedBBox.max.y() << "]\n"
2633				<< "\tz: [" << projectedBBox.min.z() << "," << projectedBBox.max.z() << "]\n"
2634			<< "In viewport coordinates:\n"
2635				<< "\tx: [" << viewportBBoxArea.x() << ", " << viewportBBoxArea.z() << "]\n"
2636				<< "\ty: [" << viewportBBoxArea.y() << ", " << viewportBBoxArea.w() << "]\n"
2637			<< "Verifying render results within the bounding box:\n"
2638			<< tcu::TestLog::EndMessage;
2639	else
2640		m_testCtx.getLog()
2641			<< tcu::TestLog::Message
2642			<< "Verifying render result:"
2643			<< tcu::TestLog::EndMessage;
2644
2645	if (m_fbo)
2646		gl.bindFramebuffer(GL_READ_FRAMEBUFFER, **m_fbo);
2647	glu::readPixels(m_context.getRenderContext(), config.viewportPos.x(), config.viewportPos.y(), viewportSurface.getAccess());
2648
2649	genReferencePointData(config, refPoints);
2650
2651	if (m_isWidePointCase)
2652		anyError = verifyWidePointPattern(viewportSurface, refPoints, projectedBBox, logFloodCounter);
2653	else
2654		anyError = verifyNarrowPointPattern(viewportSurface, refPoints, projectedBBox, logFloodCounter);
2655
2656	if (anyError)
2657	{
2658		if (logFloodCounter < 0)
2659			m_testCtx.getLog() << tcu::TestLog::Message << "Omitted " << (-logFloodCounter) << " error descriptions." << tcu::TestLog::EndMessage;
2660
2661		m_testCtx.getLog()
2662			<< tcu::TestLog::Message
2663			<< "Image verification failed."
2664			<< tcu::TestLog::EndMessage
2665			<< tcu::TestLog::ImageSet("Images", "Image verification")
2666			<< tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess())
2667			<< tcu::TestLog::EndImageSet;
2668
2669		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
2670	}
2671	else
2672	{
2673		m_testCtx.getLog()
2674			<< tcu::TestLog::Message
2675			<< "Result image ok."
2676			<< tcu::TestLog::EndMessage
2677			<< tcu::TestLog::ImageSet("Images", "Image verification")
2678			<< tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess())
2679			<< tcu::TestLog::EndImageSet;
2680	}
2681}
2682
2683struct PointSorter
2684{
2685	bool operator() (const PointRenderCase::GeneratedPoint& a, const PointRenderCase::GeneratedPoint& b) const
2686	{
2687		if (a.center.y() < b.center.y())
2688			return true;
2689		else if (a.center.y() > b.center.y())
2690			return false;
2691		else
2692			return (a.center.x() < b.center.x());
2693	}
2694};
2695
2696void PointRenderCase::genReferencePointData (const IterationConfig& config, std::vector<GeneratedPoint>& data) const
2697{
2698	std::vector<GeneratedPoint> currentPoints;
2699
2700	// vertex shader
2701	currentPoints.resize(m_attribData.size() / 2);
2702	for (int ndx = 0; ndx < (int)currentPoints.size(); ++ndx)
2703	{
2704		currentPoints[ndx].center	= m_attribData[ndx*2].swizzle(0, 1);
2705		currentPoints[ndx].even		= (m_attribData[ndx*2 + 1].y() == 1.0f); // is green
2706		currentPoints[ndx].size		= ((m_isWidePointCase) ? ((currentPoints[ndx].even) ? 5 : 3) : 1);
2707	}
2708
2709	// tessellation
2710	if (m_hasTessellationStage)
2711	{
2712		std::vector<GeneratedPoint> tessellatedPoints;
2713
2714		tessellatedPoints.resize(currentPoints.size() * 4);
2715		for (int ndx = 0; ndx < (int)currentPoints.size(); ++ndx)
2716		{
2717			const tcu::Vec2 position = tcu::Vec2(currentPoints[ndx].center.x(), 1.0f - currentPoints[ndx].center.y()); // mirror Y
2718
2719			tessellatedPoints[4 * ndx + 0].center	= position + tcu::Vec2(-0.07f, -0.07f);
2720			tessellatedPoints[4 * ndx + 0].size		= currentPoints[ndx].size;
2721			tessellatedPoints[4 * ndx + 0].even		= currentPoints[ndx].even;
2722
2723			tessellatedPoints[4 * ndx + 1].center	= position + tcu::Vec2( 0.07f, -0.07f);
2724			tessellatedPoints[4 * ndx + 1].size		= currentPoints[ndx].size;
2725			tessellatedPoints[4 * ndx + 1].even		= currentPoints[ndx].even;
2726
2727			tessellatedPoints[4 * ndx + 2].center	= position + tcu::Vec2( 0.07f,  0.07f);
2728			tessellatedPoints[4 * ndx + 2].size		= currentPoints[ndx].size;
2729			tessellatedPoints[4 * ndx + 2].even		= currentPoints[ndx].even;
2730
2731			tessellatedPoints[4 * ndx + 3].center	= position + tcu::Vec2(-0.07f,  0.07f);
2732			tessellatedPoints[4 * ndx + 3].size		= currentPoints[ndx].size;
2733			tessellatedPoints[4 * ndx + 3].even		= currentPoints[ndx].even;
2734		}
2735
2736		currentPoints.swap(tessellatedPoints);
2737	}
2738
2739	// geometry
2740	if (m_hasGeometryStage)
2741	{
2742		std::vector<GeneratedPoint> geometryShadedPoints;
2743
2744		geometryShadedPoints.resize(currentPoints.size() * 3);
2745		for (int ndx = 0; ndx < (int)currentPoints.size(); ++ndx)
2746		{
2747			const tcu::Vec2 position = tcu::Vec2(1.0f - currentPoints[ndx].center.x(), currentPoints[ndx].center.y()); // mirror X
2748
2749			geometryShadedPoints[3 * ndx + 0].center	= position + tcu::Vec2( 0.05f,  0.03f);
2750			geometryShadedPoints[3 * ndx + 0].size		= currentPoints[ndx].size;
2751			geometryShadedPoints[3 * ndx + 0].even		= currentPoints[ndx].even;
2752
2753			geometryShadedPoints[3 * ndx + 1].center	= position + tcu::Vec2(-0.01f, -0.02f);
2754			geometryShadedPoints[3 * ndx + 1].size		= currentPoints[ndx].size;
2755			geometryShadedPoints[3 * ndx + 1].even		= currentPoints[ndx].even;
2756
2757			geometryShadedPoints[3 * ndx + 2].center	= position + tcu::Vec2(-0.05f,  0.02f);
2758			geometryShadedPoints[3 * ndx + 2].size		= currentPoints[ndx].size;
2759			geometryShadedPoints[3 * ndx + 2].even		= currentPoints[ndx].even;
2760		}
2761
2762		currentPoints.swap(geometryShadedPoints);
2763	}
2764
2765	// sort from left to right, top to bottom
2766	std::sort(currentPoints.begin(), currentPoints.end(), PointSorter());
2767
2768	// map to pattern space
2769	for (int ndx = 0; ndx < (int)currentPoints.size(); ++ndx)
2770		currentPoints[ndx].center = currentPoints[ndx].center * config.patternSize + config.patternPos;
2771
2772	currentPoints.swap(data);
2773}
2774
2775bool PointRenderCase::verifyNarrowPointPattern (const tcu::Surface& viewport, const std::vector<GeneratedPoint>& refPoints, const ProjectedBBox& bbox, int& logFloodCounter)
2776{
2777	bool anyError = false;
2778
2779	// check that there is something near each sample
2780	for (int pointNdx = 0; pointNdx < (int)refPoints.size(); ++pointNdx)
2781	{
2782		const float				epsilon		= 1.0e-6f;
2783		const GeneratedPoint&	refPoint	= refPoints[pointNdx];
2784
2785		// skip points not in the the bbox, treat boundary as "in"
2786		if (refPoint.center.x() < bbox.min.x() - epsilon ||
2787			refPoint.center.y() < bbox.min.y() - epsilon ||
2788			refPoint.center.x() > bbox.max.x() + epsilon ||
2789			refPoint.center.y() > bbox.max.y() + epsilon)
2790			continue;
2791		else
2792		{
2793			// transform to viewport coords
2794			const tcu::IVec2 pixelCenter(deRoundFloatToInt32((refPoint.center.x() * 0.5f + 0.5f) * (float)viewport.getWidth()),
2795										 deRoundFloatToInt32((refPoint.center.y() * 0.5f + 0.5f) * (float)viewport.getHeight()));
2796
2797			// find rasterized point in the result
2798			if (pixelCenter.x() < 1 || pixelCenter.y() < 1 || pixelCenter.x() >= viewport.getWidth()-1 || pixelCenter.y() >= viewport.getHeight()-1)
2799			{
2800				// viewport boundary, assume point is fine
2801			}
2802			else
2803			{
2804				const int	componentNdx	= (refPoint.even) ? (1) : (2); // analyze either green or blue channel
2805				bool		foundResult		= false;
2806
2807				// check neighborhood
2808				for (int dy = -1; dy < 2 && !foundResult; ++dy)
2809				for (int dx = -1; dx < 2 && !foundResult; ++dx)
2810				{
2811					const tcu::IVec2	testPos	(pixelCenter.x() + dx, pixelCenter.y() + dy);
2812					const tcu::RGBA		color	= viewport.getPixel(testPos.x(), testPos.y());
2813
2814					if (color.toIVec()[componentNdx] > 0)
2815						foundResult = true;
2816				}
2817
2818				if (!foundResult)
2819				{
2820					anyError = true;
2821
2822					if (--logFloodCounter >= 0)
2823					{
2824						m_testCtx.getLog()
2825							<< tcu::TestLog::Message
2826							<< "Missing point near " << pixelCenter << ", vertex coordinates=" << refPoint.center.swizzle(0, 1) << "."
2827							<< tcu::TestLog::EndMessage;
2828					}
2829				}
2830			}
2831		}
2832	}
2833
2834	return anyError;
2835}
2836
2837bool PointRenderCase::verifyWidePointPattern (const tcu::Surface& viewport, const std::vector<GeneratedPoint>& refPoints, const ProjectedBBox& bbox, int& logFloodCounter)
2838{
2839	bool anyError = false;
2840
2841	// check that there is something near each sample
2842	for (int pointNdx = 0; pointNdx < (int)refPoints.size(); ++pointNdx)
2843	{
2844		const GeneratedPoint& refPoint = refPoints[pointNdx];
2845
2846		if (refPoint.center.x() >= bbox.min.x() &&
2847			refPoint.center.y() >= bbox.min.y() &&
2848			refPoint.center.x() <= bbox.max.x() &&
2849			refPoint.center.y() <= bbox.max.y())
2850		{
2851			// point fully in the bounding box
2852			anyError |= !verifyWidePoint(viewport, refPoint, bbox, POINT_FULL, logFloodCounter);
2853		}
2854		else if (refPoint.center.x() >= bbox.min.x() + (float)refPoint.size / 2.0f &&
2855				 refPoint.center.y() >= bbox.min.y() - (float)refPoint.size / 2.0f &&
2856				 refPoint.center.x() <= bbox.max.x() + (float)refPoint.size / 2.0f &&
2857				 refPoint.center.y() <= bbox.max.y() - (float)refPoint.size / 2.0f)
2858		{
2859			// point leaks into bounding box
2860			anyError |= !verifyWidePoint(viewport, refPoint, bbox, POINT_PARTIAL, logFloodCounter);
2861		}
2862	}
2863
2864	return anyError;
2865}
2866
2867bool PointRenderCase::verifyWidePoint (const tcu::Surface& viewport, const GeneratedPoint& refPoint, const ProjectedBBox& bbox, ResultPointType pointType, int& logFloodCounter)
2868{
2869	const int			componentNdx		= (refPoint.even) ? (1) : (2);
2870	const int			halfPointSizeCeil	= (refPoint.size + 1) / 2;
2871	const int			halfPointSizeFloor	= (refPoint.size + 1) / 2;
2872	const tcu::IVec4	viewportBBoxArea	= getViewportBoundingBoxArea(bbox, tcu::IVec2(viewport.getWidth(), viewport.getHeight()), (float)refPoint.size);
2873	const tcu::IVec4	verificationArea	= tcu::IVec4(de::max(viewportBBoxArea.x(), 0),
2874														 de::max(viewportBBoxArea.y(), 0),
2875														 de::min(viewportBBoxArea.z(), viewport.getWidth()),
2876														 de::min(viewportBBoxArea.w(), viewport.getHeight()));
2877	const tcu::IVec2	pointPos			= tcu::IVec2(deRoundFloatToInt32((refPoint.center.x()*0.5f + 0.5f) * (float)viewport.getWidth()),
2878														 deRoundFloatToInt32((refPoint.center.y()*0.5f + 0.5f) * (float)viewport.getHeight()));
2879
2880	// find any fragment within the point that is inside the bbox, start search at the center
2881
2882	if (pointPos.x() >= verificationArea.x() &&
2883		pointPos.y() >= verificationArea.y() &&
2884		pointPos.x() < verificationArea.z() &&
2885		pointPos.y() < verificationArea.w())
2886	{
2887		if (viewport.getPixel(pointPos.x(), pointPos.y()).toIVec()[componentNdx])
2888			return verifyWidePointAt(pointPos, viewport, refPoint, verificationArea, pointType, componentNdx, logFloodCounter);
2889	}
2890
2891	for (int dy = -halfPointSizeCeil; dy <= halfPointSizeCeil; ++dy)
2892	for (int dx = -halfPointSizeCeil; dx <= halfPointSizeCeil; ++dx)
2893	{
2894		const tcu::IVec2 testPos = pointPos + tcu::IVec2(dx, dy);
2895
2896		if (dx == 0 && dy == 0)
2897			continue;
2898
2899		if (testPos.x() >= verificationArea.x() &&
2900			testPos.y() >= verificationArea.y() &&
2901			testPos.x() < verificationArea.z() &&
2902			testPos.y() < verificationArea.w())
2903		{
2904			if (viewport.getPixel(testPos.x(), testPos.y()).toIVec()[componentNdx])
2905				return verifyWidePointAt(testPos, viewport, refPoint, verificationArea, pointType, componentNdx, logFloodCounter);
2906		}
2907	}
2908
2909	// could not find point, this is only ok near boundaries
2910	if (pointPos.x() + halfPointSizeFloor <  verificationArea.x() - 1 ||
2911		pointPos.y() + halfPointSizeFloor <  verificationArea.y() - 1 ||
2912		pointPos.x() - halfPointSizeFloor >= verificationArea.z() - 1 ||
2913		pointPos.y() - halfPointSizeFloor >= verificationArea.w() - 1)
2914		return true;
2915
2916	if (--logFloodCounter >= 0)
2917	{
2918		m_testCtx.getLog()
2919			<< tcu::TestLog::Message
2920			<< "Missing wide point near " << pointPos << ", vertex coordinates=" << refPoint.center.swizzle(0, 1) << "."
2921			<< tcu::TestLog::EndMessage;
2922	}
2923
2924	return false;
2925}
2926
2927bool PointRenderCase::verifyWidePointAt (const tcu::IVec2& pointPos, const tcu::Surface& viewport, const GeneratedPoint& refPoint, const tcu::IVec4& bbox, ResultPointType pointType, int componentNdx, int& logFloodCounter)
2928{
2929	const int				expectedPointSize		= refPoint.size;
2930	bool					viewportClippedTop		= false;
2931	bool					viewportClippedBottom	= false;
2932	bool					primitiveClippedTop		= false;
2933	bool					primitiveClippedBottom	= false;
2934	std::vector<tcu::IVec2>	widthsUpwards;
2935	std::vector<tcu::IVec2>	widthsDownwards;
2936	std::vector<tcu::IVec2>	widths;
2937
2938	// search upwards
2939	for (int y = pointPos.y();; --y)
2940	{
2941		if (y < bbox.y() || y < 0)
2942		{
2943			if (y < bbox.y())
2944				primitiveClippedTop = true;
2945			if (y < 0)
2946				viewportClippedTop = true;
2947			break;
2948		}
2949		else if (pointPos.y() - y > expectedPointSize)
2950		{
2951			// no need to go further than point height
2952			break;
2953		}
2954		else if (viewport.getPixel(pointPos.x(), y).toIVec()[componentNdx] == 0)
2955		{
2956			break;
2957		}
2958		else
2959		{
2960			widthsUpwards.push_back(scanPointWidthAt(tcu::IVec2(pointPos.x(), y), viewport, expectedPointSize, componentNdx));
2961		}
2962	}
2963
2964	// top is clipped
2965	if ((viewportClippedTop || (pointType == POINT_PARTIAL && primitiveClippedTop)) && !widthsUpwards.empty())
2966	{
2967		const tcu::IVec2&	range			= widthsUpwards.back();
2968		const bool			squareFits		= (range.y() - range.x() + 1) >= expectedPointSize;
2969		const bool			widthClipped	= (pointType == POINT_PARTIAL) && (range.x() <= bbox.x() || range.y() >= bbox.z());
2970
2971		if (squareFits || widthClipped)
2972			return true;
2973	}
2974
2975	// and downwards
2976	for (int y = pointPos.y()+1;; ++y)
2977	{
2978		if (y >= bbox.w() || y >= viewport.getHeight())
2979		{
2980			if (y >= bbox.w())
2981				primitiveClippedBottom = true;
2982			if (y >= viewport.getHeight())
2983				viewportClippedBottom = true;
2984			break;
2985		}
2986		else if (y - pointPos.y() > expectedPointSize)
2987		{
2988			// no need to go further than point height
2989			break;
2990		}
2991		else if (viewport.getPixel(pointPos.x(), y).toIVec()[componentNdx] == 0)
2992		{
2993			break;
2994		}
2995		else
2996		{
2997			widthsDownwards.push_back(scanPointWidthAt(tcu::IVec2(pointPos.x(), y), viewport, expectedPointSize, componentNdx));
2998		}
2999	}
3000
3001	// bottom is clipped
3002	if ((viewportClippedBottom || (pointType == POINT_PARTIAL && primitiveClippedBottom)) && !(widthsDownwards.empty() && widthsUpwards.empty()))
3003	{
3004		const tcu::IVec2&	range			= (widthsDownwards.empty()) ? (widthsUpwards.front()) : (widthsDownwards.back());
3005		const bool			squareFits		= (range.y() - range.x() + 1) >= expectedPointSize;
3006		const bool			bboxClipped		= (pointType == POINT_PARTIAL) && (range.x() <= bbox.x() || range.y() >= bbox.z()-1);
3007		const bool			viewportClipped	= range.x() <= 0 || range.y() >= viewport.getWidth()-1;
3008
3009		if (squareFits || bboxClipped || viewportClipped)
3010			return true;
3011	}
3012
3013	// would square point would fit into the rasterized area
3014
3015	for (int ndx = 0; ndx < (int)widthsUpwards.size(); ++ndx)
3016		widths.push_back(widthsUpwards[(int)widthsUpwards.size() - ndx - 1]);
3017	for (int ndx = 0; ndx < (int)widthsDownwards.size(); ++ndx)
3018		widths.push_back(widthsDownwards[ndx]);
3019	DE_ASSERT(!widths.empty());
3020
3021	for (int y = 0; y < (int)widths.size() - expectedPointSize + 1; ++y)
3022	{
3023		tcu::IVec2 unionRange = widths[y];
3024
3025		for (int dy = 1; dy < expectedPointSize; ++dy)
3026		{
3027			unionRange.x() = de::max(unionRange.x(), widths[y+dy].x());
3028			unionRange.y() = de::min(unionRange.y(), widths[y+dy].y());
3029		}
3030
3031		// would a N x N block fit here?
3032		{
3033			const bool squareFits		= (unionRange.y() - unionRange.x() + 1) >= expectedPointSize;
3034			const bool bboxClipped		= (pointType == POINT_PARTIAL) && (unionRange.x() <= bbox.x() || unionRange.y() >= bbox.z()-1);
3035			const bool viewportClipped	= unionRange.x() <= 0 || unionRange.y() >= viewport.getWidth()-1;
3036
3037			if (squareFits || bboxClipped || viewportClipped)
3038				return true;
3039		}
3040	}
3041
3042	if (--logFloodCounter >= 0)
3043	{
3044		m_testCtx.getLog()
3045			<< tcu::TestLog::Message
3046			<< "Missing " << expectedPointSize << "x" << expectedPointSize << " point near " << pointPos << ", vertex coordinates=" << refPoint.center.swizzle(0, 1) << "."
3047			<< tcu::TestLog::EndMessage;
3048	}
3049	return false;
3050}
3051
3052tcu::IVec2 PointRenderCase::scanPointWidthAt (const tcu::IVec2& pointPos, const tcu::Surface& viewport, int expectedPointSize, int componentNdx) const
3053{
3054	int minX = pointPos.x();
3055	int maxX = pointPos.x();
3056
3057	// search horizontally for a point edges
3058	for (int x = pointPos.x()-1; x >= 0; --x)
3059	{
3060		if (viewport.getPixel(x, pointPos.y()).toIVec()[componentNdx] == 0)
3061			break;
3062
3063		// no need to go further than point width
3064		if (pointPos.x() - x > expectedPointSize)
3065			break;
3066
3067		minX = x;
3068	}
3069	for (int x = pointPos.x()+1; x < viewport.getWidth(); ++x)
3070	{
3071		if (viewport.getPixel(x, pointPos.y()).toIVec()[componentNdx] == 0)
3072			break;
3073
3074		// no need to go further than point width
3075		if (x - pointPos.x() > expectedPointSize)
3076			break;
3077
3078		maxX = x;
3079	}
3080
3081	return tcu::IVec2(minX, maxX);
3082}
3083
3084class BlitFboCase : public TestCase
3085{
3086public:
3087	enum RenderTarget
3088	{
3089		TARGET_DEFAULT = 0,
3090		TARGET_FBO,
3091
3092		TARGET_LAST
3093	};
3094
3095							BlitFboCase						(Context& context, const char* name, const char* description, RenderTarget src, RenderTarget dst);
3096							~BlitFboCase					(void);
3097
3098private:
3099	enum
3100	{
3101		FBO_SIZE = 256,
3102	};
3103
3104	struct BlitArgs
3105	{
3106		tcu::IVec4	src;
3107		tcu::IVec4	dst;
3108		tcu::Vec4	bboxMin;
3109		tcu::Vec4	bboxMax;
3110		bool		linear;
3111	};
3112
3113	void							init					(void);
3114	void							deinit					(void);
3115	IterateResult					iterate					(void);
3116
3117	void							fillSourceWithPattern	(void);
3118	bool							verifyImage				(const BlitArgs& args);
3119
3120	const RenderTarget				m_src;
3121	const RenderTarget				m_dst;
3122
3123	std::vector<BlitArgs>			m_iterations;
3124	int								m_iteration;
3125	de::MovePtr<glu::Framebuffer>	m_srcFbo;
3126	de::MovePtr<glu::Framebuffer>	m_dstFbo;
3127	de::MovePtr<glu::Renderbuffer>	m_srcRbo;
3128	de::MovePtr<glu::Renderbuffer>	m_dstRbo;
3129	de::MovePtr<glu::ShaderProgram>	m_program;
3130	de::MovePtr<glu::Buffer>		m_vbo;
3131};
3132
3133BlitFboCase::BlitFboCase (Context& context, const char* name, const char* description, RenderTarget src, RenderTarget dst)
3134	: TestCase		(context, name, description)
3135	, m_src			(src)
3136	, m_dst			(dst)
3137	, m_iteration	(0)
3138{
3139	DE_ASSERT(src < TARGET_LAST);
3140	DE_ASSERT(dst < TARGET_LAST);
3141}
3142
3143BlitFboCase::~BlitFboCase (void)
3144{
3145	deinit();
3146}
3147
3148void BlitFboCase::init (void)
3149{
3150	const int				numIterations			= 12;
3151	const bool				defaultFBMultisampled	= (m_context.getRenderTarget().getNumSamples() > 1);
3152	const glw::Functions&	gl						= m_context.getRenderContext().getFunctions();
3153	de::Random				rnd						(0xABC123);
3154
3155	m_testCtx.getLog()
3156		<< tcu::TestLog::Message
3157		<< "Using BlitFramebuffer to blit area from "
3158			<< ((m_src == TARGET_DEFAULT) ? ("default fb") : ("fbo"))
3159			<< " to "
3160			<< ((m_dst == TARGET_DEFAULT) ? ("default fb") : ("fbo"))
3161			<< ".\n"
3162		<< "Varying blit arguments and primitive bounding box between iterations.\n"
3163		<< "Expecting bounding box to have no effect on blitting.\n"
3164		<< "Source framebuffer is filled with green-yellow grid.\n"
3165		<< tcu::TestLog::EndMessage;
3166
3167	if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box"))
3168		throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
3169	if (m_dst == TARGET_DEFAULT && defaultFBMultisampled)
3170		throw tcu::NotSupportedError("Test requires non-multisampled default framebuffer");
3171
3172	// resources
3173
3174	if (m_src == TARGET_FBO)
3175	{
3176		m_srcRbo = de::MovePtr<glu::Renderbuffer>(new glu::Renderbuffer(m_context.getRenderContext()));
3177		gl.bindRenderbuffer(GL_RENDERBUFFER, **m_srcRbo);
3178		gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, FBO_SIZE, FBO_SIZE);
3179		GLU_EXPECT_NO_ERROR(gl.getError(), "src rbo");
3180
3181		m_srcFbo = de::MovePtr<glu::Framebuffer>(new glu::Framebuffer(m_context.getRenderContext()));
3182		gl.bindFramebuffer(GL_FRAMEBUFFER, **m_srcFbo);
3183		gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, **m_srcRbo);
3184		GLU_EXPECT_NO_ERROR(gl.getError(), "src fbo");
3185	}
3186
3187	if (m_dst == TARGET_FBO)
3188	{
3189		m_dstRbo = de::MovePtr<glu::Renderbuffer>(new glu::Renderbuffer(m_context.getRenderContext()));
3190		gl.bindRenderbuffer(GL_RENDERBUFFER, **m_dstRbo);
3191		gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, FBO_SIZE, FBO_SIZE);
3192		GLU_EXPECT_NO_ERROR(gl.getError(), "dst rbo");
3193
3194		m_dstFbo = de::MovePtr<glu::Framebuffer>(new glu::Framebuffer(m_context.getRenderContext()));
3195		gl.bindFramebuffer(GL_FRAMEBUFFER, **m_dstFbo);
3196		gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, **m_dstRbo);
3197		GLU_EXPECT_NO_ERROR(gl.getError(), "dst fbo");
3198	}
3199
3200	{
3201		static const char* const s_vertexSource =	"#version 310 es\n"
3202													"in highp vec4 a_position;\n"
3203													"out highp vec4 v_position;\n"
3204													"void main()\n"
3205													"{\n"
3206													"	gl_Position = a_position;\n"
3207													"	v_position = a_position;\n"
3208													"}\n";
3209		static const char* const s_fragmentSource =	"#version 310 es\n"
3210													"in mediump vec4 v_position;\n"
3211													"layout(location=0) out mediump vec4 dEQP_FragColor;\n"
3212													"void main()\n"
3213													"{\n"
3214													"	const mediump vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n"
3215													"	const mediump vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n"
3216													"	dEQP_FragColor = (step(0.1, mod(v_position.x, 0.2)) == step(0.1, mod(v_position.y, 0.2))) ? (green) : (yellow);\n"
3217													"}\n";
3218
3219		m_program = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::VertexSource(s_vertexSource) << glu::FragmentSource(s_fragmentSource)));
3220
3221		if (!m_program->isOk())
3222		{
3223			m_testCtx.getLog() << *m_program;
3224			throw tcu::TestError("failed to build program");
3225		}
3226	}
3227
3228	{
3229		static const tcu::Vec4 s_quadCoords[] =
3230		{
3231			tcu::Vec4(-1.0f, -1.0f, 0.0f, 1.0f),
3232			tcu::Vec4(-1.0f,  1.0f, 0.0f, 1.0f),
3233			tcu::Vec4( 1.0f, -1.0f, 0.0f, 1.0f),
3234			tcu::Vec4( 1.0f,  1.0f, 0.0f, 1.0f),
3235		};
3236
3237		m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext()));
3238
3239		gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
3240		gl.bufferData(GL_ARRAY_BUFFER, sizeof(s_quadCoords), s_quadCoords, GL_STATIC_DRAW);
3241		GLU_EXPECT_NO_ERROR(gl.getError(), "set buf");
3242	}
3243
3244	// gen iterations
3245
3246	{
3247		const tcu::IVec2 srcSize = (m_src == TARGET_DEFAULT) ? (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) : (tcu::IVec2(FBO_SIZE, FBO_SIZE));
3248		const tcu::IVec2 dstSize = (m_dst == TARGET_DEFAULT) ? (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) : (tcu::IVec2(FBO_SIZE, FBO_SIZE));
3249
3250		m_testCtx.getLog()
3251			<< tcu::TestLog::Message
3252			<< "srcSize = " << srcSize << "\n"
3253			<< "dstSize = " << dstSize << "\n"
3254			<< tcu::TestLog::EndMessage;
3255
3256		for (int ndx = 0; ndx < numIterations; ++ndx)
3257		{
3258			BlitArgs args;
3259
3260			if (m_src == TARGET_DEFAULT && defaultFBMultisampled)
3261			{
3262				const tcu::IVec2	unionSize	= tcu::IVec2(de::min(srcSize.x(), dstSize.x()), de::min(srcSize.y(), dstSize.y()));
3263				const int			srcWidth	= rnd.getInt(1, unionSize.x());
3264				const int			srcHeight	= rnd.getInt(1, unionSize.y());
3265				const int			srcX		= rnd.getInt(0, unionSize.x() - srcWidth);
3266				const int			srcY		= rnd.getInt(0, unionSize.y() - srcHeight);
3267
3268				args.src.x() = srcX;
3269				args.src.y() = srcY;
3270				args.src.z() = srcX + srcWidth;
3271				args.src.w() = srcY + srcHeight;
3272
3273				args.dst = args.src;
3274			}
3275			else
3276			{
3277				const int	srcWidth	= rnd.getInt(1, srcSize.x());
3278				const int	srcHeight	= rnd.getInt(1, srcSize.y());
3279				const int	srcX		= rnd.getInt(0, srcSize.x() - srcWidth);
3280				const int	srcY		= rnd.getInt(0, srcSize.y() - srcHeight);
3281				const int	dstWidth	= rnd.getInt(1, dstSize.x());
3282				const int	dstHeight	= rnd.getInt(1, dstSize.y());
3283				const int	dstX		= rnd.getInt(-(dstWidth / 2), dstSize.x() - (dstWidth+1) / 2);		// allow dst go out of bounds
3284				const int	dstY		= rnd.getInt(-(dstHeight / 2), dstSize.y() - (dstHeight+1)  / 2);
3285
3286				args.src.x() = srcX;
3287				args.src.y() = srcY;
3288				args.src.z() = srcX + srcWidth;
3289				args.src.w() = srcY + srcHeight;
3290				args.dst.x() = dstX;
3291				args.dst.y() = dstY;
3292				args.dst.z() = dstX + dstWidth;
3293				args.dst.w() = dstY + dstHeight;
3294			}
3295
3296			args.bboxMin.x() = rnd.getFloat(-1.1f, 1.1f);
3297			args.bboxMin.y() = rnd.getFloat(-1.1f, 1.1f);
3298			args.bboxMin.z() = rnd.getFloat(-1.1f, 1.1f);
3299			args.bboxMin.w() = rnd.getFloat( 0.9f, 1.1f);
3300
3301			args.bboxMax.x() = rnd.getFloat(-1.1f, 1.1f);
3302			args.bboxMax.y() = rnd.getFloat(-1.1f, 1.1f);
3303			args.bboxMax.z() = rnd.getFloat(-1.1f, 1.1f);
3304			args.bboxMax.w() = rnd.getFloat( 0.9f, 1.1f);
3305
3306			if (args.bboxMin.x() / args.bboxMin.w() > args.bboxMax.x() / args.bboxMax.w())
3307				std::swap(args.bboxMin.x(), args.bboxMax.x());
3308			if (args.bboxMin.y() / args.bboxMin.w() > args.bboxMax.y() / args.bboxMax.w())
3309				std::swap(args.bboxMin.y(), args.bboxMax.y());
3310			if (args.bboxMin.z() / args.bboxMin.w() > args.bboxMax.z() / args.bboxMax.w())
3311				std::swap(args.bboxMin.z(), args.bboxMax.z());
3312
3313			args.linear = rnd.getBool();
3314
3315			m_iterations.push_back(args);
3316		}
3317	}
3318}
3319
3320void BlitFboCase::deinit (void)
3321{
3322	m_srcFbo.clear();
3323	m_srcRbo.clear();
3324	m_dstFbo.clear();
3325	m_dstRbo.clear();
3326	m_program.clear();
3327	m_vbo.clear();
3328}
3329
3330BlitFboCase::IterateResult BlitFboCase::iterate (void)
3331{
3332	const tcu::ScopedLogSection	section		(m_testCtx.getLog(), "Iteration" + de::toString(m_iteration), "Iteration " + de::toString(m_iteration+1) + " / " + de::toString((int)m_iterations.size()));
3333	const BlitArgs&				blitCfg		= m_iterations[m_iteration];
3334	const glw::Functions&		gl			= m_context.getRenderContext().getFunctions();
3335
3336	if (m_iteration == 0)
3337		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
3338
3339	// fill source with test pattern. Default fb must be filled for each iteration because contents might not survive the swap
3340	if (m_src == TARGET_DEFAULT || m_iteration == 0)
3341		fillSourceWithPattern();
3342
3343	m_testCtx.getLog()
3344		<< tcu::TestLog::Message
3345		<< "Set bounding box:\n"
3346		<< "\tmin:" << blitCfg.bboxMin << "\n"
3347		<< "\tmax:" << blitCfg.bboxMax << "\n"
3348		<< "Blit:\n"
3349		<<	"\tsrc: " << blitCfg.src << "\n"
3350		<<	"\tdst: " << blitCfg.dst << "\n"
3351		<<	"\tfilter: " << ((blitCfg.linear) ? ("linear") : ("nearest"))
3352		<< tcu::TestLog::EndMessage;
3353
3354	gl.primitiveBoundingBoxEXT(blitCfg.bboxMin.x(), blitCfg.bboxMin.y(), blitCfg.bboxMin.z(), blitCfg.bboxMin.w(),
3355							   blitCfg.bboxMax.x(), blitCfg.bboxMax.y(), blitCfg.bboxMax.z(), blitCfg.bboxMax.w());
3356
3357	gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, (m_dst == TARGET_FBO) ? (**m_dstFbo) : (m_context.getRenderContext().getDefaultFramebuffer()));
3358	gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
3359	gl.clear(GL_COLOR_BUFFER_BIT);
3360
3361	gl.bindFramebuffer(GL_READ_FRAMEBUFFER, (m_src == TARGET_FBO) ? (**m_srcFbo) : (m_context.getRenderContext().getDefaultFramebuffer()));
3362	gl.blitFramebuffer(blitCfg.src.x(), blitCfg.src.y(), blitCfg.src.z(), blitCfg.src.w(),
3363					   blitCfg.dst.x(), blitCfg.dst.y(), blitCfg.dst.z(), blitCfg.dst.w(),
3364					   GL_COLOR_BUFFER_BIT,
3365					   ((blitCfg.linear) ? (GL_LINEAR) : (GL_NEAREST)));
3366	GLU_EXPECT_NO_ERROR(gl.getError(), "blit");
3367
3368	if (!verifyImage(blitCfg))
3369		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got unexpected blit result");
3370
3371	return (++m_iteration == (int)m_iterations.size()) ? (STOP) : (CONTINUE);
3372}
3373
3374bool BlitFboCase::verifyImage (const BlitArgs& args)
3375{
3376	const int				colorThreshold	= 4; //!< this test case is not about how color is preserved, allow almost anything
3377	const tcu::IVec2		dstSize			= (m_dst == TARGET_DEFAULT) ? (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) : (tcu::IVec2(FBO_SIZE, FBO_SIZE));
3378	const glw::Functions&	gl				= m_context.getRenderContext().getFunctions();
3379	tcu::Surface			viewport		(dstSize.x(), dstSize.y());
3380	tcu::Surface			errorMask		(dstSize.x(), dstSize.y());
3381	bool					anyError		= false;
3382
3383	m_testCtx.getLog()
3384		<< tcu::TestLog::Message
3385		<< "Verifying blit result"
3386		<< tcu::TestLog::EndMessage;
3387
3388	gl.bindFramebuffer(GL_READ_FRAMEBUFFER, (m_dst == TARGET_FBO) ? (**m_dstFbo) : (m_context.getRenderContext().getDefaultFramebuffer()));
3389	glu::readPixels(m_context.getRenderContext(), 0, 0, viewport.getAccess());
3390
3391	tcu::clear(errorMask.getAccess(), tcu::IVec4(0, 0, 0, 255));
3392
3393	for (int y = 0; y < dstSize.y(); ++y)
3394	for (int x = 0; x < dstSize.x(); ++x)
3395	{
3396		const tcu::RGBA color	= viewport.getPixel(x, y);
3397		const bool		inside	= (x >= args.dst.x() && x < args.dst.z() && y >= args.dst.y() && y < args.dst.w());
3398		const bool		error	= (inside) ? (color.getGreen() < 255 - colorThreshold || color.getBlue() > colorThreshold)
3399										   : (color.getRed() > colorThreshold || color.getGreen() > colorThreshold || color.getBlue() > colorThreshold);
3400
3401		if (error)
3402		{
3403			anyError = true;
3404			errorMask.setPixel(x, y, tcu::RGBA::red());
3405		}
3406	}
3407
3408	if (anyError)
3409	{
3410		m_testCtx.getLog()
3411			<< tcu::TestLog::Message
3412			<< "Image verification failed."
3413			<< tcu::TestLog::EndMessage
3414			<< tcu::TestLog::ImageSet("Images", "Image verification")
3415			<< tcu::TestLog::Image("Viewport", "Viewport contents", viewport.getAccess())
3416			<< tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess())
3417			<< tcu::TestLog::EndImageSet;
3418		return false;
3419	}
3420	else
3421	{
3422		m_testCtx.getLog()
3423			<< tcu::TestLog::Message
3424			<< "Result image ok."
3425			<< tcu::TestLog::EndMessage
3426			<< tcu::TestLog::ImageSet("Images", "Image verification")
3427			<< tcu::TestLog::Image("Viewport", "Viewport contents", viewport.getAccess())
3428			<< tcu::TestLog::EndImageSet;
3429		return true;
3430	}
3431}
3432
3433void BlitFboCase::fillSourceWithPattern (void)
3434{
3435	const glw::Functions&	gl			= m_context.getRenderContext().getFunctions();
3436	const tcu::IVec2		srcSize		= (m_src == TARGET_DEFAULT) ? (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) : (tcu::IVec2(FBO_SIZE, FBO_SIZE));
3437	const int				posLocation	= gl.getAttribLocation(m_program->getProgram(), "a_position");
3438
3439	gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, (m_src == TARGET_FBO) ? (**m_srcFbo) : (m_context.getRenderContext().getDefaultFramebuffer()));
3440	gl.viewport(0, 0, srcSize.x(), srcSize.y());
3441	gl.useProgram(m_program->getProgram());
3442
3443	gl.clearColor(0.0f, 0.0f, 1.0f, 1.0f);
3444	gl.clear(GL_COLOR_BUFFER_BIT);
3445
3446	gl.enableVertexAttribArray(posLocation);
3447	gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, 4 * (int)sizeof(float), NULL);
3448	gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4);
3449	GLU_EXPECT_NO_ERROR(gl.getError(), "draw");
3450}
3451
3452class DepthDrawCase : public TestCase
3453{
3454public:
3455	enum DepthType
3456	{
3457		DEPTH_BUILTIN = 0,
3458		DEPTH_USER_DEFINED,
3459
3460		DEPTH_LAST
3461	};
3462	enum BBoxState
3463	{
3464		STATE_GLOBAL = 0,
3465		STATE_PER_PRIMITIVE,
3466
3467		STATE_LAST
3468	};
3469	enum BBoxSize
3470	{
3471		BBOX_EQUAL = 0,
3472		BBOX_LARGER,
3473
3474		BBOX_LAST
3475	};
3476
3477									DepthDrawCase					(Context& context, const char* name, const char* description, DepthType depthType, BBoxState state, BBoxSize bboxSize);
3478									~DepthDrawCase					(void);
3479
3480private:
3481	void							init							(void);
3482	void							deinit							(void);
3483	IterateResult					iterate							(void);
3484
3485	std::string						genVertexSource					(void) const;
3486	std::string						genFragmentSource				(void) const;
3487	std::string						genTessellationControlSource	(void) const;
3488	std::string						genTessellationEvaluationSource	(void) const;
3489	void							generateAttributeData			(std::vector<tcu::Vec4>& data) const;
3490	bool							verifyImage						(const tcu::Surface& viewport) const;
3491
3492	enum
3493	{
3494		RENDER_AREA_SIZE = 256,
3495	};
3496
3497	struct LayerInfo
3498	{
3499		float		zOffset;
3500		float		zScale;
3501		tcu::Vec4	color1;
3502		tcu::Vec4	color2;
3503	};
3504
3505	const int						m_numLayers;
3506	const int						m_gridSize;
3507
3508	const DepthType					m_depthType;
3509	const BBoxState					m_state;
3510	const BBoxSize					m_bboxSize;
3511
3512	de::MovePtr<glu::ShaderProgram>	m_program;
3513	de::MovePtr<glu::Buffer>		m_vbo;
3514	std::vector<LayerInfo>			m_layers;
3515};
3516
3517DepthDrawCase::DepthDrawCase (Context& context, const char* name, const char* description, DepthType depthType, BBoxState state, BBoxSize bboxSize)
3518	: TestCase		(context, name, description)
3519	, m_numLayers	(14)
3520	, m_gridSize	(24)
3521	, m_depthType	(depthType)
3522	, m_state		(state)
3523	, m_bboxSize	(bboxSize)
3524{
3525	DE_ASSERT(depthType < DEPTH_LAST);
3526	DE_ASSERT(state < STATE_LAST);
3527	DE_ASSERT(bboxSize < BBOX_LAST);
3528}
3529
3530DepthDrawCase::~DepthDrawCase (void)
3531{
3532	deinit();
3533}
3534
3535void DepthDrawCase::init (void)
3536{
3537	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
3538
3539	// requirements
3540
3541	if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box"))
3542		throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
3543	if (m_state == STATE_PER_PRIMITIVE && !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader"))
3544		throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader extension");
3545	if (m_context.getRenderTarget().getDepthBits() == 0)
3546		throw tcu::NotSupportedError("Test requires depth buffer");
3547	if (m_context.getRenderTarget().getWidth() < RENDER_AREA_SIZE || m_context.getRenderTarget().getHeight() < RENDER_AREA_SIZE)
3548		throw tcu::NotSupportedError("Test requires " + de::toString<int>(RENDER_AREA_SIZE) + "x" + de::toString<int>(RENDER_AREA_SIZE) + " viewport");
3549
3550	// log
3551	m_testCtx.getLog()
3552		<< tcu::TestLog::Message
3553		<< "Rendering multiple triangle grids with with different z coordinates.\n"
3554		<< "Topmost grid is green-yellow, other grids are blue-red.\n"
3555		<< "Expecting only the green-yellow grid to be visible.\n"
3556		<< "Setting primitive bounding box "
3557			<< ((m_bboxSize == BBOX_EQUAL) ? ("to exactly cover") : ("to cover"))
3558			<< ((m_state == STATE_GLOBAL) ? (" each grid") : (" each triangle"))
3559			<< ((m_bboxSize == BBOX_EQUAL) ? (".") : (" and include some padding."))
3560			<< "\n"
3561		<< "Set bounding box using "
3562			<< ((m_state == STATE_GLOBAL) ? ("PRIMITIVE_BOUNDING_BOX_EXT state") : ("gl_BoundingBoxEXT output"))
3563			<< "\n"
3564		<< ((m_depthType == DEPTH_USER_DEFINED) ? ("Fragment depth is set in the fragment shader") : (""))
3565		<< tcu::TestLog::EndMessage;
3566
3567	// resources
3568
3569	{
3570		glu::ProgramSources sources;
3571		sources << glu::VertexSource(genVertexSource());
3572		sources << glu::FragmentSource(genFragmentSource());
3573
3574		if (m_state == STATE_PER_PRIMITIVE)
3575			sources << glu::TessellationControlSource(genTessellationControlSource())
3576					<< glu::TessellationEvaluationSource(genTessellationEvaluationSource());
3577
3578		m_program = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(), sources));
3579		GLU_EXPECT_NO_ERROR(gl.getError(), "build program");
3580
3581		{
3582			const tcu::ScopedLogSection section(m_testCtx.getLog(), "ShaderProgram", "Shader program");
3583			m_testCtx.getLog() << *m_program;
3584		}
3585
3586		if (!m_program->isOk())
3587			throw tcu::TestError("failed to build program");
3588	}
3589
3590	{
3591		std::vector<tcu::Vec4> data;
3592
3593		generateAttributeData(data);
3594
3595		m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext()));
3596		gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
3597		gl.bufferData(GL_ARRAY_BUFFER, (int)(sizeof(tcu::Vec4) * data.size()), &data[0], GL_STATIC_DRAW);
3598		GLU_EXPECT_NO_ERROR(gl.getError(), "buf upload");
3599	}
3600
3601	// gen layers
3602	{
3603		de::Random rnd(0x12345);
3604
3605		m_layers.resize(m_numLayers);
3606		for (int layerNdx = 0; layerNdx < m_numLayers; ++layerNdx)
3607		{
3608			m_layers[layerNdx].zOffset	= ((float)layerNdx / (float)m_numLayers) * 2.0f - 1.0f;
3609			m_layers[layerNdx].zScale	= (2.0f / (float)m_numLayers);
3610			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));
3611			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));
3612		}
3613		rnd.shuffle(m_layers.begin(), m_layers.end());
3614	}
3615}
3616
3617void DepthDrawCase::deinit (void)
3618{
3619	m_program.clear();
3620	m_vbo.clear();
3621}
3622
3623DepthDrawCase::IterateResult DepthDrawCase::iterate (void)
3624{
3625	const bool				hasTessellation		= (m_state == STATE_PER_PRIMITIVE);
3626	const glw::Functions&	gl					= m_context.getRenderContext().getFunctions();
3627	const glw::GLint		posLocation			= gl.getAttribLocation(m_program->getProgram(), "a_position");
3628	const glw::GLint		colLocation			= gl.getAttribLocation(m_program->getProgram(), "a_colorMix");
3629	const glw::GLint		depthBiasLocation	= gl.getUniformLocation(m_program->getProgram(), "u_depthBias");
3630	const glw::GLint		depthScaleLocation	= gl.getUniformLocation(m_program->getProgram(), "u_depthScale");
3631	const glw::GLint		color1Location		= gl.getUniformLocation(m_program->getProgram(), "u_color1");
3632	const glw::GLint		color2Location		= gl.getUniformLocation(m_program->getProgram(), "u_color2");
3633
3634	tcu::Surface			viewport			(RENDER_AREA_SIZE, RENDER_AREA_SIZE);
3635	de::Random				rnd					(0x213237);
3636
3637	TCU_CHECK(posLocation != -1);
3638	TCU_CHECK(colLocation != -1);
3639	TCU_CHECK(depthBiasLocation != -1);
3640	TCU_CHECK(depthScaleLocation != -1);
3641	TCU_CHECK(color1Location != -1);
3642	TCU_CHECK(color2Location != -1);
3643
3644	gl.viewport(0, 0, RENDER_AREA_SIZE, RENDER_AREA_SIZE);
3645	gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
3646	gl.clearDepthf(1.0f);
3647	gl.depthFunc(GL_LESS);
3648	gl.enable(GL_DEPTH_TEST);
3649	gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
3650	GLU_EXPECT_NO_ERROR(gl.getError(), "setup viewport");
3651
3652	gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
3653	gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, (int)(8 * sizeof(float)), (const float*)DE_NULL);
3654	gl.vertexAttribPointer(colLocation, 4, GL_FLOAT, GL_FALSE, (int)(8 * sizeof(float)), (const float*)DE_NULL + 4);
3655	gl.enableVertexAttribArray(posLocation);
3656	gl.enableVertexAttribArray(colLocation);
3657	gl.useProgram(m_program->getProgram());
3658	GLU_EXPECT_NO_ERROR(gl.getError(), "setup va");
3659
3660	if (hasTessellation)
3661		gl.patchParameteri(GL_PATCH_VERTICES, 3);
3662
3663	for (int layerNdx = 0; layerNdx < m_numLayers; ++layerNdx)
3664	{
3665		gl.uniform1f(depthBiasLocation, m_layers[layerNdx].zOffset);
3666		gl.uniform1f(depthScaleLocation, m_layers[layerNdx].zScale);
3667		gl.uniform4fv(color1Location, 1, m_layers[layerNdx].color1.getPtr());
3668		gl.uniform4fv(color2Location, 1, m_layers[layerNdx].color2.getPtr());
3669
3670		if (m_state == STATE_GLOBAL)
3671		{
3672			const float negPadding = (m_bboxSize == BBOX_EQUAL) ? (0.0f) : (rnd.getFloat() * 0.3f);
3673			const float posPadding = (m_bboxSize == BBOX_EQUAL) ? (0.0f) : (rnd.getFloat() * 0.3f);
3674
3675			gl.primitiveBoundingBoxEXT(-1.0f, -1.0f, m_layers[layerNdx].zOffset - negPadding, 1.0f,
3676									    1.0f,  1.0f, (m_layers[layerNdx].zOffset + m_layers[layerNdx].zScale + posPadding), 1.0f);
3677		}
3678
3679		gl.drawArrays((hasTessellation) ? (GL_PATCHES) : (GL_TRIANGLES), 0, m_gridSize * m_gridSize * 6);
3680	}
3681
3682	glu::readPixels(m_context.getRenderContext(), 0, 0, viewport.getAccess());
3683	GLU_EXPECT_NO_ERROR(gl.getError(), "render and read");
3684
3685	if (verifyImage(viewport))
3686		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
3687	else
3688		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
3689
3690	return STOP;
3691}
3692
3693std::string DepthDrawCase::genVertexSource (void) const
3694{
3695	const bool			hasTessellation	= (m_state == STATE_PER_PRIMITIVE);
3696	std::ostringstream	buf;
3697
3698	buf <<	"#version 310 es\n"
3699			"in highp vec4 a_position;\n"
3700			"in highp vec4 a_colorMix;\n"
3701			"out highp vec4 vtx_colorMix;\n";
3702
3703	if (!hasTessellation && m_depthType == DEPTH_USER_DEFINED)
3704		buf << "out highp float v_fragDepth;\n";
3705
3706	if (!hasTessellation)
3707		buf <<	"uniform highp float u_depthBias;\n"
3708				"uniform highp float u_depthScale;\n";
3709
3710	buf <<	"\n"
3711			"void main()\n"
3712			"{\n";
3713
3714	if (hasTessellation)
3715		buf << "	gl_Position = a_position;\n";
3716	else if (m_depthType == DEPTH_USER_DEFINED)
3717		buf <<	"	highp float dummyZ = a_position.z;\n"
3718				"	highp float writtenZ = a_position.w;\n"
3719				"	gl_Position = vec4(a_position.xy, dummyZ, 1.0);\n"
3720				"	v_fragDepth = writtenZ * u_depthScale + u_depthBias;\n";
3721	else
3722		buf <<	"	highp float writtenZ = a_position.w;\n"
3723				"	gl_Position = vec4(a_position.xy, writtenZ * u_depthScale + u_depthBias, 1.0);\n";
3724
3725	buf <<	"	vtx_colorMix = a_colorMix;\n"
3726			"}\n";
3727
3728	return buf.str();
3729}
3730
3731std::string DepthDrawCase::genFragmentSource (void) const
3732{
3733	const bool			hasTessellation	= (m_state == STATE_PER_PRIMITIVE);
3734	const char* const	colorMixName	= (hasTessellation) ? ("tess_eval_colorMix") : ("vtx_colorMix");
3735	std::ostringstream	buf;
3736
3737	buf <<	"#version 310 es\n"
3738			"in mediump vec4 " << colorMixName << ";\n";
3739
3740	if (m_depthType == DEPTH_USER_DEFINED)
3741		buf << "in mediump float v_fragDepth;\n";
3742
3743	buf <<	"layout(location = 0) out mediump vec4 o_color;\n"
3744			"uniform highp vec4 u_color1;\n"
3745			"uniform highp vec4 u_color2;\n"
3746			"\n"
3747			"void main()\n"
3748			"{\n"
3749			"	o_color = mix(u_color1, u_color2, " << colorMixName << ");\n";
3750
3751	if (m_depthType == DEPTH_USER_DEFINED)
3752		buf << "	gl_FragDepth = v_fragDepth * 0.5 + 0.5;\n";
3753
3754	buf <<	"}\n";
3755
3756	return buf.str();
3757}
3758
3759std::string DepthDrawCase::genTessellationControlSource (void) const
3760{
3761	std::ostringstream	buf;
3762
3763	buf <<	"#version 310 es\n"
3764			"#extension GL_EXT_tessellation_shader : require\n"
3765			"#extension GL_EXT_primitive_bounding_box : require\n"
3766			"layout(vertices=3) out;\n"
3767			"\n"
3768			"uniform highp float u_depthBias;\n"
3769			"uniform highp float u_depthScale;\n"
3770			"\n"
3771			"in highp vec4 vtx_colorMix[];\n"
3772			"out highp vec4 tess_ctrl_colorMix[];\n"
3773			"\n"
3774			"void main()\n"
3775			"{\n"
3776			"	gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
3777			"	tess_ctrl_colorMix[gl_InvocationID] = vtx_colorMix[0];\n"
3778			"\n"
3779			"	gl_TessLevelOuter[0] = 2.8;\n"
3780			"	gl_TessLevelOuter[1] = 2.8;\n"
3781			"	gl_TessLevelOuter[2] = 2.8;\n"
3782			"	gl_TessLevelInner[0] = 2.8;\n"
3783			"\n"
3784			"	// real Z stored in w component\n"
3785			"	highp vec4 minBound = vec4(min(min(vec3(gl_in[0].gl_Position.xy, gl_in[0].gl_Position.w * u_depthScale + u_depthBias),\n"
3786			"	                                   vec3(gl_in[1].gl_Position.xy, gl_in[1].gl_Position.w * u_depthScale + u_depthBias)),\n"
3787			"	                               vec3(gl_in[2].gl_Position.xy, gl_in[2].gl_Position.w * u_depthScale + u_depthBias)), 1.0);\n"
3788			"	highp vec4 maxBound = vec4(max(max(vec3(gl_in[0].gl_Position.xy, gl_in[0].gl_Position.w * u_depthScale + u_depthBias),\n"
3789			"	                                   vec3(gl_in[1].gl_Position.xy, gl_in[1].gl_Position.w * u_depthScale + u_depthBias)),\n"
3790			"	                               vec3(gl_in[2].gl_Position.xy, gl_in[2].gl_Position.w * u_depthScale + u_depthBias)), 1.0);\n";
3791
3792	if (m_bboxSize == BBOX_EQUAL)
3793		buf <<	"	gl_BoundingBoxEXT[0] = minBound;\n"
3794				"	gl_BoundingBoxEXT[1] = maxBound;\n";
3795	else
3796		buf <<	"	highp float nedPadding = mod(gl_in[0].gl_Position.z, 0.3);\n"
3797				"	highp float posPadding = mod(gl_in[1].gl_Position.z, 0.3);\n"
3798				"	gl_BoundingBoxEXT[0] = minBound - vec4(0.0, 0.0, nedPadding, 0.0);\n"
3799				"	gl_BoundingBoxEXT[1] = maxBound + vec4(0.0, 0.0, posPadding, 0.0);\n";
3800
3801	buf <<	"}\n";
3802
3803	return buf.str();
3804}
3805
3806std::string DepthDrawCase::genTessellationEvaluationSource (void) const
3807{
3808	std::ostringstream	buf;
3809
3810	buf <<	"#version 310 es\n"
3811			"#extension GL_EXT_tessellation_shader : require\n"
3812			"#extension GL_EXT_gpu_shader5 : require\n"
3813			"layout(triangles) in;\n"
3814			"\n"
3815			"in highp vec4 tess_ctrl_colorMix[];\n"
3816			"out highp vec4 tess_eval_colorMix;\n";
3817
3818	if (m_depthType == DEPTH_USER_DEFINED)
3819		buf << "out highp float v_fragDepth;\n";
3820
3821	buf <<	"uniform highp float u_depthBias;\n"
3822			"uniform highp float u_depthScale;\n"
3823			"\n"
3824			"precise gl_Position;\n"
3825			"\n"
3826			"void main()\n"
3827			"{\n"
3828			"	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";
3829
3830	if (m_depthType == DEPTH_USER_DEFINED)
3831		buf <<	"	highp float dummyZ = tessellatedPos.z;\n"
3832				"	highp float writtenZ = tessellatedPos.w;\n"
3833				"	gl_Position = vec4(tessellatedPos.xy, dummyZ, 1.0);\n"
3834				"	v_fragDepth = writtenZ * u_depthScale + u_depthBias;\n";
3835	else
3836		buf <<	"	highp float writtenZ = tessellatedPos.w;\n"
3837				"	gl_Position = vec4(tessellatedPos.xy, writtenZ * u_depthScale + u_depthBias, 1.0);\n";
3838
3839	buf <<	"	tess_eval_colorMix = tess_ctrl_colorMix[0];\n"
3840			"}\n";
3841
3842	return buf.str();
3843}
3844
3845void DepthDrawCase::generateAttributeData (std::vector<tcu::Vec4>& data) const
3846{
3847	const tcu::Vec4		color1				(0.0f, 0.0f, 0.0f, 0.0f); // mix weights
3848	const tcu::Vec4		color2				(1.0f, 1.0f, 1.0f, 1.0f);
3849	std::vector<int>	cellOrder			(m_gridSize * m_gridSize);
3850	de::Random			rnd					(0xAB54321);
3851
3852	// generate grid with cells in random order
3853	for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
3854		cellOrder[ndx] = ndx;
3855	rnd.shuffle(cellOrder.begin(), cellOrder.end());
3856
3857	data.resize(m_gridSize * m_gridSize * 6 * 2);
3858	for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
3859	{
3860		const int			cellNdx		= cellOrder[ndx];
3861		const int			cellX		= cellNdx % m_gridSize;
3862		const int			cellY		= cellNdx / m_gridSize;
3863		const tcu::Vec4&	cellColor	= ((cellX+cellY)%2 == 0) ? (color1) : (color2);
3864
3865		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;
3866		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;
3867		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;
3868		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;
3869		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;
3870		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;
3871
3872		// Fill Z with random values (fake Z)
3873		for (int vtxNdx = 0; vtxNdx < 6; ++vtxNdx)
3874			data[ndx * 6 * 2 + 2*vtxNdx].z() = rnd.getFloat(0.0f, 1.0);
3875
3876		// Fill W with other random values (written Z)
3877		for (int vtxNdx = 0; vtxNdx < 6; ++vtxNdx)
3878			data[ndx * 6 * 2 + 2*vtxNdx].w() = rnd.getFloat(0.0f, 1.0);
3879	}
3880}
3881
3882bool DepthDrawCase::verifyImage (const tcu::Surface& viewport) const
3883{
3884	tcu::Surface	errorMask	(viewport.getWidth(), viewport.getHeight());
3885	bool			anyError	= false;
3886
3887	tcu::clear(errorMask.getAccess(), tcu::IVec4(0,0,0,255));
3888
3889	for (int y = 0; y < viewport.getHeight(); ++y)
3890	for (int x = 0; x < viewport.getWidth(); ++x)
3891	{
3892		const tcu::RGBA	pixel		= viewport.getPixel(x, y);
3893		bool			error		= false;
3894
3895		// expect green, yellow or a combination of these
3896		if (pixel.getGreen() != 255 || pixel.getBlue() != 0)
3897			error = true;
3898
3899		if (error)
3900		{
3901			errorMask.setPixel(x, y, tcu::RGBA::red());
3902			anyError = true;
3903		}
3904	}
3905
3906	if (anyError)
3907		m_testCtx.getLog()
3908			<< tcu::TestLog::Message
3909			<< "Image verification failed."
3910			<< tcu::TestLog::EndMessage
3911			<< tcu::TestLog::ImageSet("Images", "Image verification")
3912			<< tcu::TestLog::Image("Viewport", "Viewport contents", viewport.getAccess())
3913			<< tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess())
3914			<< tcu::TestLog::EndImageSet;
3915	else
3916		m_testCtx.getLog()
3917			<< tcu::TestLog::Message
3918			<< "Result image ok."
3919			<< tcu::TestLog::EndMessage
3920			<< tcu::TestLog::ImageSet("Images", "Image verification")
3921			<< tcu::TestLog::Image("Viewport", "Viewport contents", viewport.getAccess())
3922			<< tcu::TestLog::EndImageSet;
3923
3924	return !anyError;
3925}
3926
3927class ClearCase : public TestCase
3928{
3929public:
3930	enum
3931	{
3932		SCISSOR_CLEAR_BIT		= 1 << 0,
3933		DRAW_TRIANGLE_BIT		= 1 << 1,
3934		PER_PRIMITIVE_BBOX_BIT	= 1 << 2,
3935		FULLSCREEN_SCISSOR_BIT	= 1 << 3,
3936	};
3937
3938									ClearCase						(Context& context, const char* name, const char* description, deUint32 flags);
3939									~ClearCase						(void);
3940
3941private:
3942	struct DrawObject
3943	{
3944		int firstNdx;
3945		int numVertices;
3946	};
3947
3948	void							init							(void);
3949	void							deinit							(void);
3950	IterateResult					iterate							(void);
3951
3952	void							createVbo						(void);
3953	void							createProgram					(void);
3954	void							renderTo						(tcu::Surface& dst, bool useBBox);
3955	bool							verifyImagesEqual				(const tcu::PixelBufferAccess& withoutBBox, const tcu::PixelBufferAccess& withBBox);
3956	bool							verifyImageResultValid			(const tcu::PixelBufferAccess& result);
3957
3958	std::string						genVertexSource					(void) const;
3959	std::string						genFragmentSource				(void) const;
3960	std::string						genTessellationControlSource	(bool setBBox) const;
3961	std::string						genTessellationEvaluationSource	(void) const;
3962
3963	const bool						m_scissoredClear;
3964	const bool						m_fullscreenScissor;
3965	const bool						m_drawTriangles;
3966	const bool						m_useGlobalState;
3967
3968	de::MovePtr<glu::Buffer>		m_vbo;
3969	de::MovePtr<glu::ShaderProgram>	m_perPrimitiveProgram;
3970	de::MovePtr<glu::ShaderProgram>	m_basicProgram;
3971	std::vector<DrawObject>			m_drawObjects;
3972	std::vector<tcu::Vec4>			m_objectVertices;
3973};
3974
3975ClearCase::ClearCase (Context& context, const char* name, const char* description, deUint32 flags)
3976	: TestCase				(context, name, description)
3977	, m_scissoredClear		((flags & SCISSOR_CLEAR_BIT) != 0)
3978	, m_fullscreenScissor	((flags & FULLSCREEN_SCISSOR_BIT) != 0)
3979	, m_drawTriangles		((flags & DRAW_TRIANGLE_BIT) != 0)
3980	, m_useGlobalState		((flags & PER_PRIMITIVE_BBOX_BIT) == 0)
3981{
3982	DE_ASSERT(m_useGlobalState || m_drawTriangles); // per-triangle bbox requires triangles
3983	DE_ASSERT(!m_fullscreenScissor || m_scissoredClear); // fullscreenScissor requires scissoredClear
3984}
3985
3986ClearCase::~ClearCase (void)
3987{
3988	deinit();
3989}
3990
3991void ClearCase::init (void)
3992{
3993	if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box"))
3994		throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
3995	if (m_drawTriangles && !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader"))
3996		throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader extension");
3997
3998	m_testCtx.getLog()
3999		<< tcu::TestLog::Message
4000		<< "Doing multiple"
4001			<< ((m_scissoredClear) ? (" scissored") : (""))
4002			<< " color buffer clears"
4003			<< ((m_drawTriangles) ? (" and drawing some geometry between them") : (""))
4004			<< ".\n"
4005		<< ((m_scissoredClear && m_fullscreenScissor) ? ("Setting scissor area to cover entire viewport.\n") : (""))
4006		<< "Rendering with and without setting the bounding box.\n"
4007		<< "Expecting bounding box to have no effect on clears (i.e. results are constant).\n"
4008		<< "Set bounding box using "
4009			<< ((m_useGlobalState) ? ("PRIMITIVE_BOUNDING_BOX_EXT state") : ("gl_BoundingBoxEXT output"))
4010			<< ".\n"
4011		<< "Clear color is green with yellowish shades.\n"
4012		<< ((m_drawTriangles) ? ("Primitive color is yellow with greenish shades.\n") : (""))
4013		<< tcu::TestLog::EndMessage;
4014
4015	if (m_drawTriangles)
4016	{
4017		createVbo();
4018		createProgram();
4019	}
4020}
4021
4022void ClearCase::deinit (void)
4023{
4024	m_vbo.clear();
4025	m_perPrimitiveProgram.clear();
4026	m_basicProgram.clear();
4027	m_drawObjects = std::vector<DrawObject>();
4028	m_objectVertices = std::vector<tcu::Vec4>();
4029}
4030
4031ClearCase::IterateResult ClearCase::iterate (void)
4032{
4033	const tcu::IVec2	renderTargetSize	(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight());
4034	tcu::Surface		resultWithoutBBox	(renderTargetSize.x(), renderTargetSize.y());
4035	tcu::Surface		resultWithBBox		(renderTargetSize.x(), renderTargetSize.y());
4036
4037	// render with and without bbox set
4038	for (int passNdx = 0; passNdx < 2; ++passNdx)
4039	{
4040		const bool		useBBox			= (passNdx == 1);
4041		tcu::Surface&	destination		= (useBBox) ? (resultWithBBox) : (resultWithoutBBox);
4042
4043		renderTo(destination, useBBox);
4044	}
4045
4046	// Verify images are equal and that the image does not contain (trivially detectable) garbage
4047
4048	if (!verifyImagesEqual(resultWithoutBBox.getAccess(), resultWithBBox.getAccess()))
4049	{
4050		// verifyImagesEqual will print out the image and error mask
4051		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
4052	}
4053	else if (!verifyImageResultValid(resultWithBBox.getAccess()))
4054	{
4055		// verifyImageResultValid will print out the image and error mask
4056		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Result verification failed");
4057	}
4058	else
4059	{
4060		m_testCtx.getLog()
4061			<< tcu::TestLog::Message
4062			<< "Image comparison passed."
4063			<< tcu::TestLog::EndMessage
4064			<< tcu::TestLog::ImageSet("Images", "Image verification")
4065			<< tcu::TestLog::Image("Result", "Result", resultWithBBox.getAccess())
4066			<< tcu::TestLog::EndImageSet;
4067
4068		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
4069	}
4070
4071	return STOP;
4072}
4073
4074void ClearCase::createVbo (void)
4075{
4076	const int				numObjects	= 16;
4077	const glw::Functions&	gl			= m_context.getRenderContext().getFunctions();
4078	de::Random				rnd			(deStringHash(getName()));
4079
4080	m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext()));
4081
4082	for (int objectNdx = 0; objectNdx < numObjects; ++objectNdx)
4083	{
4084		const int	numTriangles	= rnd.getInt(1, 4);
4085		const float	minX			= rnd.getFloat(-1.2f, 0.8f);
4086		const float	minY			= rnd.getFloat(-1.2f, 0.8f);
4087		const float	maxX			= minX + rnd.getFloat(0.2f, 1.0f);
4088		const float	maxY			= minY + rnd.getFloat(0.2f, 1.0f);
4089
4090		DrawObject	drawObject;
4091		drawObject.firstNdx = (int)m_objectVertices.size();
4092		drawObject.numVertices = numTriangles * 3;
4093
4094		m_drawObjects.push_back(drawObject);
4095
4096		for (int triangleNdx = 0; triangleNdx < numTriangles; ++triangleNdx)
4097		for (int vertexNdx = 0; vertexNdx < 3; ++vertexNdx)
4098		{
4099			const float posX = rnd.getFloat(minX, maxX);
4100			const float posY = rnd.getFloat(minY, maxY);
4101			const float posZ = rnd.getFloat(-0.7f, 0.7f);
4102			const float posW = rnd.getFloat(0.9f, 1.1f);
4103
4104			m_objectVertices.push_back(tcu::Vec4(posX, posY, posZ, posW));
4105		}
4106	}
4107
4108	gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
4109	gl.bufferData(GL_ARRAY_BUFFER, (int)(m_objectVertices.size() * sizeof(tcu::Vec4)), &m_objectVertices[0], GL_STATIC_DRAW);
4110	GLU_EXPECT_NO_ERROR(gl.getError(), "buffer upload");
4111}
4112
4113void ClearCase::createProgram (void)
4114{
4115	m_basicProgram = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(),
4116																			glu::ProgramSources()
4117																				<< glu::VertexSource(genVertexSource())
4118																				<< glu::FragmentSource(genFragmentSource())
4119																				<< glu::TessellationControlSource(genTessellationControlSource(false))
4120																				<< glu::TessellationEvaluationSource(genTessellationEvaluationSource())));
4121
4122	m_testCtx.getLog()
4123		<< tcu::TestLog::Section("Program", "Shader program")
4124		<< *m_basicProgram
4125		<< tcu::TestLog::EndSection;
4126
4127	if (!m_basicProgram->isOk())
4128		throw tcu::TestError("shader build failed");
4129
4130	if (!m_useGlobalState)
4131	{
4132		m_perPrimitiveProgram = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(),
4133																					   glu::ProgramSources()
4134																							<< glu::VertexSource(genVertexSource())
4135																							<< glu::FragmentSource(genFragmentSource())
4136																							<< glu::TessellationControlSource(genTessellationControlSource(true))
4137																							<< glu::TessellationEvaluationSource(genTessellationEvaluationSource())));
4138
4139		m_testCtx.getLog()
4140			<< tcu::TestLog::Section("PerPrimitiveProgram", "Shader program that sets the bounding box")
4141			<< *m_perPrimitiveProgram
4142			<< tcu::TestLog::EndSection;
4143
4144		if (!m_perPrimitiveProgram->isOk())
4145			throw tcu::TestError("shader build failed");
4146	}
4147}
4148
4149void ClearCase::renderTo (tcu::Surface& dst, bool useBBox)
4150{
4151	const int				numOps				= 45;
4152	const tcu::Vec4			yellow				(1.0f, 1.0f, 0.0f, 1.0f);
4153	const tcu::Vec4			green				(0.0f, 1.0f, 0.0f, 1.0f);
4154	const tcu::IVec2		renderTargetSize	(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight());
4155	const glw::Functions&	gl					= m_context.getRenderContext().getFunctions();
4156	de::Random				rnd					(deStringHash(getName()));
4157	glu::VertexArray		vao					(m_context.getRenderContext());
4158
4159	// always do the initial clear
4160	gl.disable(GL_SCISSOR_TEST);
4161	gl.viewport(0, 0, renderTargetSize.x(), renderTargetSize.y());
4162	gl.clearColor(yellow.x(), yellow.y(), yellow.z(), yellow.w());
4163	gl.clear(GL_COLOR_BUFFER_BIT);
4164	gl.finish();
4165
4166	// prepare draw
4167	if (m_scissoredClear)
4168		gl.enable(GL_SCISSOR_TEST);
4169
4170	if (m_drawTriangles)
4171	{
4172		const deUint32	programHandle		= (m_useGlobalState || !useBBox) ? (m_basicProgram->getProgram()) : (m_perPrimitiveProgram->getProgram());
4173		const int		positionAttribLoc	= gl.getAttribLocation(programHandle, "a_position");
4174
4175		TCU_CHECK(positionAttribLoc != -1);
4176
4177		gl.useProgram(programHandle);
4178		gl.bindVertexArray(*vao);
4179		gl.enableVertexAttribArray(positionAttribLoc);
4180		gl.vertexAttribPointer(positionAttribLoc, 4, GL_FLOAT, GL_FALSE, (int)sizeof(tcu::Vec4), DE_NULL);
4181		gl.patchParameteri(GL_PATCH_VERTICES, 3);
4182	}
4183
4184	// do random scissor/clearldraw operations
4185	for (int opNdx = 0; opNdx < numOps; ++opNdx)
4186	{
4187		const int	drawObjNdx				= (m_drawTriangles) ? (rnd.getInt(0, (int)m_drawObjects.size() - 1)) : (0);
4188		const int	objectVertexStartNdx	= (m_drawTriangles) ? (m_drawObjects[drawObjNdx].firstNdx) : (0);
4189		const int	objectVertexLength		= (m_drawTriangles) ? (m_drawObjects[drawObjNdx].numVertices) : (0);
4190		tcu::Vec4	bboxMin;
4191		tcu::Vec4	bboxMax;
4192
4193		if (m_drawTriangles)
4194		{
4195			bboxMin = tcu::Vec4(1.0f, 1.0f, 1.0f, 1.0f);
4196			bboxMax = tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.0f);
4197
4198			// calc bbox
4199			for (int vertexNdx = objectVertexStartNdx; vertexNdx < objectVertexStartNdx + objectVertexLength; ++vertexNdx)
4200			for (int componentNdx = 0; componentNdx < 4; ++componentNdx)
4201			{
4202				bboxMin[componentNdx] = de::min(bboxMin[componentNdx], m_objectVertices[vertexNdx][componentNdx]);
4203				bboxMax[componentNdx] = de::max(bboxMax[componentNdx], m_objectVertices[vertexNdx][componentNdx]);
4204			}
4205		}
4206		else
4207		{
4208			// no geometry, just random something
4209			bboxMin.x() = rnd.getFloat(-1.2f, 1.0f);
4210			bboxMin.y() = rnd.getFloat(-1.2f, 1.0f);
4211			bboxMin.z() = rnd.getFloat(-1.2f, 1.0f);
4212			bboxMin.w() = 1.0f;
4213			bboxMax.x() = bboxMin.x() + rnd.getFloat(0.2f, 1.0f);
4214			bboxMax.y() = bboxMin.y() + rnd.getFloat(0.2f, 1.0f);
4215			bboxMax.z() = bboxMin.z() + rnd.getFloat(0.2f, 1.0f);
4216			bboxMax.w() = 1.0f;
4217		}
4218
4219		if (m_scissoredClear)
4220		{
4221			const int scissorX = (m_fullscreenScissor) ? (0)					: rnd.getInt(0, renderTargetSize.x()-1);
4222			const int scissorY = (m_fullscreenScissor) ? (0)					: rnd.getInt(0, renderTargetSize.y()-1);
4223			const int scissorW = (m_fullscreenScissor) ? (renderTargetSize.x())	: rnd.getInt(0, renderTargetSize.x()-scissorX);
4224			const int scissorH = (m_fullscreenScissor) ? (renderTargetSize.y())	: rnd.getInt(0, renderTargetSize.y()-scissorY);
4225
4226			gl.scissor(scissorX, scissorY, scissorW, scissorH);
4227		}
4228
4229		{
4230			const tcu::Vec4 color = tcu::mix(green, yellow, rnd.getFloat() * 0.4f); // greenish
4231			gl.clearColor(color.x(), color.y(), color.z(), color.w());
4232			gl.clear(GL_COLOR_BUFFER_BIT);
4233		}
4234
4235		if (useBBox)
4236		{
4237			DE_ASSERT(m_useGlobalState || m_drawTriangles); // !m_useGlobalState -> m_drawTriangles
4238			if (m_useGlobalState)
4239				gl.primitiveBoundingBoxEXT(bboxMin.x(), bboxMin.y(), bboxMin.z(), bboxMin.w(),
4240										   bboxMax.x(), bboxMax.y(), bboxMax.z(), bboxMax.w());
4241		}
4242
4243		if (m_drawTriangles)
4244			gl.drawArrays(GL_PATCHES, objectVertexStartNdx, objectVertexLength);
4245	}
4246
4247	GLU_EXPECT_NO_ERROR(gl.getError(), "post draw");
4248	glu::readPixels(m_context.getRenderContext(), 0, 0, dst.getAccess());
4249}
4250
4251bool ClearCase::verifyImagesEqual (const tcu::PixelBufferAccess& withoutBBox, const tcu::PixelBufferAccess& withBBox)
4252{
4253	DE_ASSERT(withoutBBox.getWidth() == withBBox.getWidth());
4254	DE_ASSERT(withoutBBox.getHeight() == withBBox.getHeight());
4255
4256	tcu::Surface	errorMask	(withoutBBox.getWidth(), withoutBBox.getHeight());
4257	bool			anyError	= false;
4258
4259	tcu::clear(errorMask.getAccess(), tcu::RGBA::green().toIVec());
4260
4261	for (int y = 0; y < withoutBBox.getHeight(); ++y)
4262	for (int x = 0; x < withoutBBox.getWidth(); ++x)
4263	{
4264		if (withoutBBox.getPixelInt(x, y) != withBBox.getPixelInt(x, y))
4265		{
4266			errorMask.setPixel(x, y, tcu::RGBA::red());
4267			anyError = true;
4268		}
4269	}
4270
4271	if (anyError)
4272	{
4273		m_testCtx.getLog()
4274			<< tcu::TestLog::Message
4275			<< "Image comparison failed."
4276			<< tcu::TestLog::EndMessage
4277			<< tcu::TestLog::ImageSet("Images", "Image comparison")
4278			<< tcu::TestLog::Image("WithoutBBox", "Result with bounding box not set", withoutBBox)
4279			<< tcu::TestLog::Image("WithBBox", "Result with bounding box set", withBBox)
4280			<< tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess())
4281			<< tcu::TestLog::EndImageSet;
4282	}
4283
4284	return !anyError;
4285}
4286
4287bool ClearCase::verifyImageResultValid (const tcu::PixelBufferAccess& result)
4288{
4289	tcu::Surface	errorMask	(result.getWidth(), result.getHeight());
4290	bool			anyError	= false;
4291
4292	tcu::clear(errorMask.getAccess(), tcu::RGBA::green().toIVec());
4293
4294	for (int y = 0; y < result.getHeight(); ++y)
4295	for (int x = 0; x < result.getWidth(); ++x)
4296	{
4297		const tcu::IVec4 pixel = result.getPixelInt(x, y);
4298
4299		// allow green, yellow and any shade between
4300		if (pixel[1] != 255 || pixel[2] != 0)
4301		{
4302			errorMask.setPixel(x, y, tcu::RGBA::red());
4303			anyError = true;
4304		}
4305	}
4306
4307	if (anyError)
4308	{
4309		m_testCtx.getLog()
4310			<< tcu::TestLog::Message
4311			<< "Image verification failed."
4312			<< tcu::TestLog::EndMessage
4313			<< tcu::TestLog::ImageSet("Images", "Image verification")
4314			<< tcu::TestLog::Image("ResultImage", "Result image", result)
4315			<< tcu::TestLog::Image("ErrorMask", "Error mask", errorMask)
4316			<< tcu::TestLog::EndImageSet;
4317	}
4318
4319	return !anyError;
4320}
4321
4322static const char* const s_yellowishPosOnlyVertexSource =	"#version 310 es\n"
4323															"in highp vec4 a_position;\n"
4324															"out highp vec4 v_vertex_color;\n"
4325															"void main()\n"
4326															"{\n"
4327															"	gl_Position = a_position;\n"
4328															"	// yellowish shade\n"
4329															"	highp float redComponent = 0.5 + float(gl_VertexID % 5) / 8.0;\n"
4330															"	v_vertex_color = vec4(redComponent, 1.0, 0.0, 1.0);\n"
4331															"}\n";
4332
4333static const char* const s_basicColorFragmentSource =	"#version 310 es\n"
4334														"in mediump vec4 v_color;\n"
4335														"layout(location = 0) out mediump vec4 o_color;\n"
4336														"void main()\n"
4337														"{\n"
4338														"	o_color = v_color;\n"
4339														"}\n";
4340
4341
4342static const char* const s_basicColorTessEvalSource =	"#version 310 es\n"
4343														"#extension GL_EXT_tessellation_shader : require\n"
4344														"#extension GL_EXT_gpu_shader5 : require\n"
4345														"layout(triangles) in;\n"
4346														"in highp vec4 v_tess_eval_color[];\n"
4347														"out highp vec4 v_color;\n"
4348														"precise gl_Position;\n"
4349														"void main()\n"
4350														"{\n"
4351														"	gl_Position = gl_TessCoord.x * gl_in[0].gl_Position\n"
4352														"	            + gl_TessCoord.y * gl_in[1].gl_Position\n"
4353														"	            + gl_TessCoord.z * gl_in[2].gl_Position;\n"
4354														"	v_color = gl_TessCoord.x * v_tess_eval_color[0]\n"
4355														"	        + gl_TessCoord.y * v_tess_eval_color[1]\n"
4356														"	        + gl_TessCoord.z * v_tess_eval_color[2];\n"
4357														"}\n";
4358
4359std::string ClearCase::genVertexSource (void) const
4360{
4361	return	s_yellowishPosOnlyVertexSource;
4362}
4363
4364std::string ClearCase::genFragmentSource (void) const
4365{
4366	return s_basicColorFragmentSource;
4367}
4368
4369std::string ClearCase::genTessellationControlSource (bool setBBox) const
4370{
4371	std::ostringstream buf;
4372
4373	buf <<	"#version 310 es\n"
4374			"#extension GL_EXT_tessellation_shader : require\n";
4375
4376	if (setBBox)
4377		buf << "#extension GL_EXT_primitive_bounding_box : require\n";
4378
4379	buf <<	"layout(vertices=3) out;\n"
4380			"in highp vec4 v_vertex_color[];\n"
4381			"out highp vec4 v_tess_eval_color[];\n"
4382			"void main()\n"
4383			"{\n"
4384			"	gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
4385			"	v_tess_eval_color[gl_InvocationID] = v_vertex_color[gl_InvocationID];\n"
4386			"	gl_TessLevelOuter[0] = 2.8;\n"
4387			"	gl_TessLevelOuter[1] = 2.8;\n"
4388			"	gl_TessLevelOuter[2] = 2.8;\n"
4389			"	gl_TessLevelInner[0] = 2.8;\n";
4390
4391	if (setBBox)
4392	{
4393		buf <<	"\n"
4394		"	gl_BoundingBoxEXT[0] = min(min(gl_in[0].gl_Position,\n"
4395		"	                               gl_in[1].gl_Position),\n"
4396		"	                           gl_in[2].gl_Position);\n"
4397		"	gl_BoundingBoxEXT[1] = max(max(gl_in[0].gl_Position,\n"
4398		"	                               gl_in[1].gl_Position),\n"
4399		"	                           gl_in[2].gl_Position);\n";
4400	}
4401
4402	buf << "}\n";
4403	return buf.str();
4404}
4405
4406std::string ClearCase::genTessellationEvaluationSource (void) const
4407{
4408	return s_basicColorTessEvalSource;
4409}
4410
4411class ViewportCallOrderCase : public TestCase
4412{
4413public:
4414	enum CallOrder
4415	{
4416		VIEWPORT_FIRST = 0,
4417		BBOX_FIRST,
4418
4419		ORDER_LAST
4420	};
4421
4422									ViewportCallOrderCase			(Context& context, const char* name, const char* description, CallOrder callOrder);
4423									~ViewportCallOrderCase			(void);
4424
4425private:
4426	void							init							(void);
4427	void							deinit							(void);
4428	IterateResult					iterate							(void);
4429
4430	void							genVbo							(void);
4431	void							genProgram						(void);
4432	bool							verifyImage						(const tcu::PixelBufferAccess& result);
4433
4434	std::string						genVertexSource					(void) const;
4435	std::string						genFragmentSource				(void) const;
4436	std::string						genTessellationControlSource	(void) const;
4437	std::string						genTessellationEvaluationSource	(void) const;
4438
4439	const CallOrder					m_callOrder;
4440
4441	de::MovePtr<glu::Buffer>		m_vbo;
4442	de::MovePtr<glu::ShaderProgram>	m_program;
4443	int								m_numVertices;
4444};
4445
4446ViewportCallOrderCase::ViewportCallOrderCase (Context& context, const char* name, const char* description, CallOrder callOrder)
4447	: TestCase		(context, name, description)
4448	, m_callOrder	(callOrder)
4449	, m_numVertices	(-1)
4450{
4451	DE_ASSERT(m_callOrder < ORDER_LAST);
4452}
4453
4454ViewportCallOrderCase::~ViewportCallOrderCase (void)
4455{
4456	deinit();
4457}
4458
4459void ViewportCallOrderCase::init (void)
4460{
4461	if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box"))
4462		throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
4463
4464	m_testCtx.getLog()
4465		<< tcu::TestLog::Message
4466		<< "Testing call order of state setting functions have no effect on the rendering.\n"
4467		<< "Setting viewport and bounding box in the following order:\n"
4468			<< ((m_callOrder == VIEWPORT_FIRST)
4469				? ("\tFirst viewport with glViewport function.\n")
4470				: ("\tFirst bounding box with glPrimitiveBoundingBoxEXT function.\n"))
4471			<< ((m_callOrder == VIEWPORT_FIRST)
4472				? ("\tThen bounding box with glPrimitiveBoundingBoxEXT function.\n")
4473				: ("\tThen viewport with glViewport function.\n"))
4474		<< "Verifying rendering result."
4475		<< tcu::TestLog::EndMessage;
4476
4477	// resources
4478	genVbo();
4479	genProgram();
4480}
4481
4482void ViewportCallOrderCase::deinit (void)
4483{
4484	m_vbo.clear();
4485	m_program.clear();
4486}
4487
4488ViewportCallOrderCase::IterateResult ViewportCallOrderCase::iterate (void)
4489{
4490	const glw::Functions&	gl				= m_context.getRenderContext().getFunctions();
4491	const tcu::IVec2		viewportSize	= tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight());
4492	const glw::GLint		posLocation		= gl.getAttribLocation(m_program->getProgram(), "a_position");
4493	tcu::Surface			resultSurface	(viewportSize.x(), viewportSize.y());
4494
4495	gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
4496	gl.clear(GL_COLOR_BUFFER_BIT);
4497
4498	// set state
4499	for (int orderNdx = 0; orderNdx < 2; ++orderNdx)
4500	{
4501		if ((orderNdx == 0 && m_callOrder == VIEWPORT_FIRST) ||
4502			(orderNdx == 1 && m_callOrder == BBOX_FIRST))
4503		{
4504			m_testCtx.getLog()
4505				<< tcu::TestLog::Message
4506				<< "Setting viewport to cover the left half of the render target.\n"
4507				<< "\t(0, 0, " << (viewportSize.x()/2) << ", " << viewportSize.y() << ")"
4508				<< tcu::TestLog::EndMessage;
4509
4510			gl.viewport(0, 0, viewportSize.x()/2, viewportSize.y());
4511		}
4512		else
4513		{
4514			m_testCtx.getLog()
4515				<< tcu::TestLog::Message
4516				<< "Setting bounding box to cover the right half of the clip space.\n"
4517				<< "\t(0.0, -1.0, -1.0, 1.0) .. (1.0, 1.0, 1.0f, 1.0)"
4518				<< tcu::TestLog::EndMessage;
4519
4520			gl.primitiveBoundingBoxEXT(0.0f, -1.0f, -1.0f, 1.0f,
4521									   1.0f,  1.0f,  1.0f, 1.0f);
4522		}
4523	}
4524
4525	m_testCtx.getLog()
4526		<< tcu::TestLog::Message
4527		<< "Rendering mesh covering the right half of the clip space."
4528		<< tcu::TestLog::EndMessage;
4529
4530	gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
4531	gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, sizeof(float[4]), (const float*)DE_NULL);
4532	gl.enableVertexAttribArray(posLocation);
4533	gl.useProgram(m_program->getProgram());
4534	gl.patchParameteri(GL_PATCH_VERTICES, 3);
4535	gl.drawArrays(GL_PATCHES, 0, m_numVertices);
4536	GLU_EXPECT_NO_ERROR(gl.getError(), "post-draw");
4537
4538	m_testCtx.getLog()
4539		<< tcu::TestLog::Message
4540		<< "Verifying image"
4541		<< tcu::TestLog::EndMessage;
4542	glu::readPixels(m_context.getRenderContext(), 0, 0, resultSurface.getAccess());
4543
4544	if (!verifyImage(resultSurface.getAccess()))
4545		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
4546	else
4547	{
4548		m_testCtx.getLog()
4549			<< tcu::TestLog::Message
4550			<< "Result ok."
4551			<< tcu::TestLog::EndMessage
4552			<< tcu::TestLog::ImageSet("Images", "Image verification")
4553			<< tcu::TestLog::Image("Result", "Result", resultSurface.getAccess())
4554			<< tcu::TestLog::EndImageSet;
4555
4556		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
4557	}
4558	return STOP;
4559}
4560
4561void ViewportCallOrderCase::genVbo (void)
4562{
4563	const int				gridSize	= 6;
4564	const glw::Functions&	gl			= m_context.getRenderContext().getFunctions();
4565	std::vector<tcu::Vec4>	data		(gridSize * gridSize * 2 * 3);
4566	std::vector<int>		cellOrder	(gridSize * gridSize * 2);
4567	de::Random				rnd			(0x55443322);
4568
4569	// generate grid with triangles in random order
4570	for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
4571		cellOrder[ndx] = ndx;
4572	rnd.shuffle(cellOrder.begin(), cellOrder.end());
4573
4574	// generate grid filling the right half of the clip space: (x: 0.0, y: -1.0) .. (x: 1.0, y: 1.0)
4575	for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
4576	{
4577		const int			cellNdx		= cellOrder[ndx];
4578		const bool			cellSide	= ((cellNdx % 2) == 0);
4579		const int			cellX		= (cellNdx / 2) % gridSize;
4580		const int			cellY		= (cellNdx / 2) / gridSize;
4581
4582		if (cellSide)
4583		{
4584			data[ndx * 3 + 0] = tcu::Vec4(float(cellX+0) / float(gridSize), (float(cellY+0) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
4585			data[ndx * 3 + 1] = tcu::Vec4(float(cellX+1) / float(gridSize), (float(cellY+1) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
4586			data[ndx * 3 + 2] = tcu::Vec4(float(cellX+0) / float(gridSize), (float(cellY+1) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
4587		}
4588		else
4589		{
4590			data[ndx * 3 + 0] = tcu::Vec4(float(cellX+0) / float(gridSize), (float(cellY+0) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
4591			data[ndx * 3 + 1] = tcu::Vec4(float(cellX+1) / float(gridSize), (float(cellY+0) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
4592			data[ndx * 3 + 2] = tcu::Vec4(float(cellX+1) / float(gridSize), (float(cellY+1) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
4593		}
4594	}
4595
4596	m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext()));
4597	gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
4598	gl.bufferData(GL_ARRAY_BUFFER, (int)(data.size() * sizeof(tcu::Vec4)), &data[0], GL_STATIC_DRAW);
4599	GLU_EXPECT_NO_ERROR(gl.getError(), "create vbo");
4600
4601	m_numVertices = (int)data.size();
4602}
4603
4604void ViewportCallOrderCase::genProgram (void)
4605{
4606	m_program = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(),
4607																	   glu::ProgramSources()
4608																			<< glu::VertexSource(genVertexSource())
4609																			<< glu::FragmentSource(genFragmentSource())
4610																			<< glu::TessellationControlSource(genTessellationControlSource())
4611																			<< glu::TessellationEvaluationSource(genTessellationEvaluationSource())));
4612
4613	m_testCtx.getLog()
4614		<< tcu::TestLog::Section("Program", "Shader program")
4615		<< *m_program
4616		<< tcu::TestLog::EndSection;
4617
4618	if (!m_program->isOk())
4619		throw tcu::TestError("shader build failed");
4620}
4621
4622bool ViewportCallOrderCase::verifyImage (const tcu::PixelBufferAccess& result)
4623{
4624	const tcu::IVec2	insideBorder	(deCeilFloatToInt32(0.25f * (float)result.getWidth()) + 1, deFloorFloatToInt32(0.5f * (float)result.getWidth()) - 1);
4625	const tcu::IVec2	outsideBorder	(deFloorFloatToInt32(0.25f * (float)result.getWidth()) - 1, deCeilFloatToInt32(0.5f * (float)result.getWidth()) + 1);
4626	tcu::Surface		errorMask		(result.getWidth(), result.getHeight());
4627	bool				anyError		= false;
4628
4629	tcu::clear(errorMask.getAccess(), tcu::RGBA::green().toIVec());
4630
4631	for (int y = 0; y < result.getHeight(); ++y)
4632	for (int x = 0; x < result.getWidth(); ++x)
4633	{
4634		const tcu::IVec4	pixel			= result.getPixelInt(x, y);
4635		const bool			insideMeshArea	= x >= insideBorder.x() && x <= insideBorder.x();
4636		const bool			outsideMeshArea = x <= outsideBorder.x() && x >= outsideBorder.x();
4637
4638		// inside mesh, allow green, yellow and any shade between
4639		// outside mesh, allow background (black) only
4640		// in the border area, allow anything
4641		if ((insideMeshArea && (pixel[1] != 255 || pixel[2] != 0)) ||
4642			(outsideMeshArea && (pixel[0] != 0 || pixel[1] != 0 || pixel[2] != 0)))
4643		{
4644			errorMask.setPixel(x, y, tcu::RGBA::red());
4645			anyError = true;
4646		}
4647	}
4648
4649	if (anyError)
4650	{
4651		m_testCtx.getLog()
4652			<< tcu::TestLog::Message
4653			<< "Image verification failed."
4654			<< tcu::TestLog::EndMessage
4655			<< tcu::TestLog::ImageSet("Images", "Image verification")
4656			<< tcu::TestLog::Image("ResultImage", "Result image", result)
4657			<< tcu::TestLog::Image("ErrorMask", "Error mask", errorMask)
4658			<< tcu::TestLog::EndImageSet;
4659	}
4660
4661	return !anyError;
4662}
4663
4664std::string ViewportCallOrderCase::genVertexSource (void) const
4665{
4666	return	s_yellowishPosOnlyVertexSource;
4667}
4668
4669std::string ViewportCallOrderCase::genFragmentSource (void) const
4670{
4671	return s_basicColorFragmentSource;
4672}
4673
4674std::string ViewportCallOrderCase::genTessellationControlSource (void) const
4675{
4676	return	"#version 310 es\n"
4677			"#extension GL_EXT_tessellation_shader : require\n"
4678			"layout(vertices=3) out;\n"
4679			"in highp vec4 v_vertex_color[];\n"
4680			"out highp vec4 v_tess_eval_color[];\n"
4681			"void main()\n"
4682			"{\n"
4683			"	gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
4684			"	v_tess_eval_color[gl_InvocationID] = v_vertex_color[gl_InvocationID];\n"
4685			"	gl_TessLevelOuter[0] = 2.8;\n"
4686			"	gl_TessLevelOuter[1] = 2.8;\n"
4687			"	gl_TessLevelOuter[2] = 2.8;\n"
4688			"	gl_TessLevelInner[0] = 2.8;\n"
4689			"}\n";
4690}
4691
4692std::string ViewportCallOrderCase::genTessellationEvaluationSource (void) const
4693{
4694	return s_basicColorTessEvalSource;
4695}
4696
4697} // anonymous
4698
4699PrimitiveBoundingBoxTests::PrimitiveBoundingBoxTests (Context& context)
4700	: TestCaseGroup(context, "primitive_bounding_box", "Tests for EXT_primitive_bounding_box")
4701{
4702}
4703
4704PrimitiveBoundingBoxTests::~PrimitiveBoundingBoxTests (void)
4705{
4706}
4707
4708void PrimitiveBoundingBoxTests::init (void)
4709{
4710	static const struct
4711	{
4712		const char*	name;
4713		const char*	description;
4714		deUint32	methodFlags;
4715	} stateSetMethods[] =
4716	{
4717		{
4718			"global_state",
4719			"Set bounding box using PRIMITIVE_BOUNDING_BOX_EXT state",
4720			BBoxRenderCase::FLAG_SET_BBOX_STATE,
4721		},
4722		{
4723			"tessellation_set_per_draw",
4724			"Set bounding box using gl_BoundingBoxEXT, use same value for all primitives",
4725			BBoxRenderCase::FLAG_SET_BBOX_OUTPUT,
4726		},
4727		{
4728			"tessellation_set_per_primitive",
4729			"Set bounding box using gl_BoundingBoxEXT, use per-primitive bounding box",
4730			BBoxRenderCase::FLAG_SET_BBOX_OUTPUT | BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
4731		},
4732	};
4733	static const struct
4734	{
4735		const char*	name;
4736		const char*	description;
4737		deUint32	stageFlags;
4738	} pipelineConfigs[] =
4739	{
4740		{
4741			"vertex_fragment",
4742			"Render with vertex-fragment program",
4743			0u
4744		},
4745		{
4746			"vertex_tessellation_fragment",
4747			"Render with vertex-tessellation{ctrl,eval}-fragment program",
4748			BBoxRenderCase::FLAG_TESSELLATION
4749		},
4750		{
4751			"vertex_geometry_fragment",
4752			"Render with vertex-tessellation{ctrl,eval}-geometry-fragment program",
4753			BBoxRenderCase::FLAG_GEOMETRY
4754		},
4755		{
4756			"vertex_tessellation_geometry_fragment",
4757			"Render with vertex-geometry-fragment program",
4758			BBoxRenderCase::FLAG_TESSELLATION | BBoxRenderCase::FLAG_GEOMETRY
4759		},
4760	};
4761	static const struct
4762	{
4763		const char*	name;
4764		const char*	description;
4765		deUint32	flags;
4766		deUint32	invalidFlags;
4767		deUint32	requiredFlags;
4768	} usageConfigs[] =
4769	{
4770		{
4771			"default_framebuffer_bbox_equal",
4772			"Render to default framebuffer, set tight bounding box",
4773			BBoxRenderCase::FLAG_RENDERTARGET_DEFAULT | BBoxRenderCase::FLAG_BBOXSIZE_EQUAL,
4774			BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
4775			0
4776		},
4777		{
4778			"default_framebuffer_bbox_larger",
4779			"Render to default framebuffer, set padded bounding box",
4780			BBoxRenderCase::FLAG_RENDERTARGET_DEFAULT | BBoxRenderCase::FLAG_BBOXSIZE_LARGER,
4781			BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
4782			0
4783		},
4784		{
4785			"default_framebuffer_bbox_smaller",
4786			"Render to default framebuffer, set too small bounding box",
4787			BBoxRenderCase::FLAG_RENDERTARGET_DEFAULT | BBoxRenderCase::FLAG_BBOXSIZE_SMALLER,
4788			BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
4789			0
4790		},
4791		{
4792			"fbo_bbox_equal",
4793			"Render to texture, set tight bounding box",
4794			BBoxRenderCase::FLAG_RENDERTARGET_FBO | BBoxRenderCase::FLAG_BBOXSIZE_EQUAL,
4795			BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
4796			0
4797		},
4798		{
4799			"fbo_bbox_larger",
4800			"Render to texture, set padded bounding box",
4801			BBoxRenderCase::FLAG_RENDERTARGET_FBO | BBoxRenderCase::FLAG_BBOXSIZE_LARGER,
4802			BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
4803			0
4804		},
4805		{
4806			"fbo_bbox_smaller",
4807			"Render to texture, set too small bounding box",
4808			BBoxRenderCase::FLAG_RENDERTARGET_FBO | BBoxRenderCase::FLAG_BBOXSIZE_SMALLER,
4809			BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
4810			0
4811		},
4812		{
4813			"default_framebuffer",
4814			"Render to default framebuffer, set tight bounding box",
4815			BBoxRenderCase::FLAG_RENDERTARGET_DEFAULT | BBoxRenderCase::FLAG_BBOXSIZE_EQUAL,
4816			0,
4817			BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX
4818		},
4819		{
4820			"fbo",
4821			"Render to texture, set tight bounding box",
4822			BBoxRenderCase::FLAG_RENDERTARGET_FBO | BBoxRenderCase::FLAG_BBOXSIZE_EQUAL,
4823			0,
4824			BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX
4825		},
4826	};
4827	enum PrimitiveRenderType
4828	{
4829		TYPE_TRIANGLE,
4830		TYPE_LINE,
4831		TYPE_POINT,
4832	};
4833	const struct
4834	{
4835		const char*			name;
4836		const char*			description;
4837		PrimitiveRenderType	type;
4838		deUint32			flags;
4839	} primitiveTypes[] =
4840	{
4841		{
4842			"triangles",
4843			"Triangle render tests",
4844			TYPE_TRIANGLE,
4845			0
4846		},
4847		{
4848			"lines",
4849			"Line render tests",
4850			TYPE_LINE,
4851			0
4852		},
4853		{
4854			"points",
4855			"Point render tests",
4856			TYPE_POINT,
4857			0
4858		},
4859		{
4860			"wide_lines",
4861			"Wide line render tests",
4862			TYPE_LINE,
4863			LineRenderCase::LINEFLAG_WIDE
4864		},
4865		{
4866			"wide_points",
4867			"Wide point render tests",
4868			TYPE_POINT,
4869			PointRenderCase::POINTFLAG_WIDE
4870		},
4871	};
4872
4873	// .state_query
4874	{
4875		tcu::TestCaseGroup* const stateQueryGroup = new tcu::TestCaseGroup(m_testCtx, "state_query", "State queries");
4876		addChild(stateQueryGroup);
4877
4878		stateQueryGroup->addChild(new InitialValueCase	(m_context,	"initial_value",	"Initial value case"));
4879		stateQueryGroup->addChild(new QueryCase			(m_context,	"getfloat",			"getFloatv",			QueryCase::QUERY_FLOAT));
4880		stateQueryGroup->addChild(new QueryCase			(m_context,	"getboolean",		"getBooleanv",			QueryCase::QUERY_BOOLEAN));
4881		stateQueryGroup->addChild(new QueryCase			(m_context,	"getinteger",		"getIntegerv",			QueryCase::QUERY_INT));
4882		stateQueryGroup->addChild(new QueryCase			(m_context,	"getinteger64",		"getInteger64v",		QueryCase::QUERY_INT64));
4883	}
4884
4885	// .triangles
4886	// .(wide_)lines
4887	// .(wide_)points
4888	for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitiveTypes); ++primitiveTypeNdx)
4889	{
4890		tcu::TestCaseGroup* const primitiveGroup = new tcu::TestCaseGroup(m_testCtx, primitiveTypes[primitiveTypeNdx].name, primitiveTypes[primitiveTypeNdx].description);
4891		addChild(primitiveGroup);
4892
4893		for (int stateSetMethodNdx = 0; stateSetMethodNdx < DE_LENGTH_OF_ARRAY(stateSetMethods); ++stateSetMethodNdx)
4894		{
4895			tcu::TestCaseGroup* const methodGroup = new tcu::TestCaseGroup(m_testCtx, stateSetMethods[stateSetMethodNdx].name, stateSetMethods[stateSetMethodNdx].description);
4896			primitiveGroup->addChild(methodGroup);
4897
4898			for (int pipelineConfigNdx = 0; pipelineConfigNdx < DE_LENGTH_OF_ARRAY(pipelineConfigs); ++pipelineConfigNdx)
4899			{
4900				if ((stateSetMethods[stateSetMethodNdx].methodFlags & BBoxRenderCase::FLAG_SET_BBOX_OUTPUT) != 0 &&
4901					(pipelineConfigs[pipelineConfigNdx].stageFlags  & BBoxRenderCase::FLAG_TESSELLATION)    == 0)
4902				{
4903					// invalid config combination
4904				}
4905				else
4906				{
4907					tcu::TestCaseGroup* const pipelineGroup = new tcu::TestCaseGroup(m_testCtx, pipelineConfigs[pipelineConfigNdx].name, pipelineConfigs[pipelineConfigNdx].description);
4908					methodGroup->addChild(pipelineGroup);
4909
4910					for (int usageNdx = 0; usageNdx < DE_LENGTH_OF_ARRAY(usageConfigs); ++usageNdx)
4911					{
4912						const deUint32 flags = primitiveTypes[primitiveTypeNdx].flags         |
4913											   stateSetMethods[stateSetMethodNdx].methodFlags |
4914											   pipelineConfigs[pipelineConfigNdx].stageFlags  |
4915											   usageConfigs[usageNdx].flags;
4916
4917						if (usageConfigs[usageNdx].invalidFlags && (flags & usageConfigs[usageNdx].invalidFlags) != 0)
4918							continue;
4919						if (usageConfigs[usageNdx].requiredFlags && (flags & usageConfigs[usageNdx].requiredFlags) == 0)
4920							continue;
4921
4922						switch (primitiveTypes[primitiveTypeNdx].type)
4923						{
4924							case TYPE_TRIANGLE:
4925								pipelineGroup->addChild(new GridRenderCase(m_context, usageConfigs[usageNdx].name, usageConfigs[usageNdx].description, flags));
4926								break;
4927							case TYPE_LINE:
4928								pipelineGroup->addChild(new LineRenderCase(m_context, usageConfigs[usageNdx].name, usageConfigs[usageNdx].description, flags));
4929								break;
4930							case TYPE_POINT:
4931								pipelineGroup->addChild(new PointRenderCase(m_context, usageConfigs[usageNdx].name, usageConfigs[usageNdx].description, flags));
4932								break;
4933							default:
4934								DE_ASSERT(false);
4935						}
4936					}
4937				}
4938			}
4939		}
4940	}
4941
4942	// .depth
4943	{
4944		static const struct
4945		{
4946			const char*					name;
4947			const char*					description;
4948			DepthDrawCase::DepthType	depthMethod;
4949		} depthMethods[] =
4950		{
4951			{
4952				"builtin_depth",
4953				"Fragment depth not modified in fragment shader",
4954				DepthDrawCase::DEPTH_BUILTIN
4955			},
4956			{
4957				"user_defined_depth",
4958				"Fragment depth is defined in the fragment shader",
4959				DepthDrawCase::DEPTH_USER_DEFINED
4960			},
4961		};
4962		static const struct
4963		{
4964			const char*					name;
4965			const char*					description;
4966			DepthDrawCase::BBoxState	bboxState;
4967			DepthDrawCase::BBoxSize		bboxSize;
4968		} depthCases[] =
4969		{
4970			{
4971				"global_state_bbox_equal",
4972				"Test tight bounding box with global bbox state",
4973				DepthDrawCase::STATE_GLOBAL,
4974				DepthDrawCase::BBOX_EQUAL,
4975			},
4976			{
4977				"global_state_bbox_larger",
4978				"Test padded bounding box with global bbox state",
4979				DepthDrawCase::STATE_GLOBAL,
4980				DepthDrawCase::BBOX_LARGER,
4981			},
4982			{
4983				"per_primitive_bbox_equal",
4984				"Test tight bounding box with tessellation output bbox",
4985				DepthDrawCase::STATE_PER_PRIMITIVE,
4986				DepthDrawCase::BBOX_EQUAL,
4987			},
4988			{
4989				"per_primitive_bbox_larger",
4990				"Test padded bounding box with tessellation output bbox",
4991				DepthDrawCase::STATE_PER_PRIMITIVE,
4992				DepthDrawCase::BBOX_LARGER,
4993			},
4994		};
4995
4996		tcu::TestCaseGroup* const depthGroup = new tcu::TestCaseGroup(m_testCtx, "depth", "Test bounding box depth component");
4997		addChild(depthGroup);
4998
4999		// .builtin_depth
5000		// .user_defined_depth
5001		for (int depthNdx = 0; depthNdx < DE_LENGTH_OF_ARRAY(depthMethods); ++depthNdx)
5002		{
5003			tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(m_testCtx, depthMethods[depthNdx].name, depthMethods[depthNdx].description);
5004			depthGroup->addChild(group);
5005
5006			for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(depthCases); ++caseNdx)
5007				group->addChild(new DepthDrawCase(m_context, depthCases[caseNdx].name, depthCases[caseNdx].description, depthMethods[depthNdx].depthMethod, depthCases[caseNdx].bboxState, depthCases[caseNdx].bboxSize));
5008		}
5009	}
5010
5011	// .blit_fbo
5012	{
5013		tcu::TestCaseGroup* const blitFboGroup = new tcu::TestCaseGroup(m_testCtx, "blit_fbo", "Test bounding box does not affect blitting");
5014		addChild(blitFboGroup);
5015
5016		blitFboGroup->addChild(new BlitFboCase(m_context, "blit_default_to_fbo", "Blit from default fb to fbo", BlitFboCase::TARGET_DEFAULT, BlitFboCase::TARGET_FBO));
5017		blitFboGroup->addChild(new BlitFboCase(m_context, "blit_fbo_to_default", "Blit from fbo to default fb", BlitFboCase::TARGET_FBO,     BlitFboCase::TARGET_DEFAULT));
5018		blitFboGroup->addChild(new BlitFboCase(m_context, "blit_fbo_to_fbo",     "Blit from fbo to fbo",        BlitFboCase::TARGET_FBO,     BlitFboCase::TARGET_FBO));
5019	}
5020
5021	// .clear
5022	{
5023		tcu::TestCaseGroup* const clearGroup = new tcu::TestCaseGroup(m_testCtx, "clear", "Test bounding box does not clears");
5024		addChild(clearGroup);
5025
5026		clearGroup->addChild(new ClearCase(m_context, "full_clear",                                             "Do full clears",                                               0));
5027		clearGroup->addChild(new ClearCase(m_context, "full_clear_with_triangles",                              "Do full clears and render some geometry",                      ClearCase::DRAW_TRIANGLE_BIT));
5028		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));
5029		clearGroup->addChild(new ClearCase(m_context, "scissored_clear",                                        "Do scissored clears",                                          ClearCase::SCISSOR_CLEAR_BIT));
5030		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));
5031		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));
5032		clearGroup->addChild(new ClearCase(m_context, "scissored_full_clear",                                   "Do full clears with enabled scissor",                          ClearCase::FULLSCREEN_SCISSOR_BIT | ClearCase::SCISSOR_CLEAR_BIT));
5033		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));
5034		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));
5035	}
5036
5037	// .call_order (Khronos bug #13262)
5038	{
5039		tcu::TestCaseGroup* const callOrderGroup = new tcu::TestCaseGroup(m_testCtx, "call_order", "Test viewport and bounding box calls have no effect");
5040		addChild(callOrderGroup);
5041
5042		callOrderGroup->addChild(new ViewportCallOrderCase(m_context, "viewport_first_bbox_second", "Set up viewport first and bbox after", ViewportCallOrderCase::VIEWPORT_FIRST));
5043		callOrderGroup->addChild(new ViewportCallOrderCase(m_context, "bbox_first_viewport_second", "Set up bbox first and viewport after", ViewportCallOrderCase::BBOX_FIRST));
5044	}
5045}
5046
5047} // Functional
5048} // gles31
5049} // deqp
5050