Home | History | Annotate | Download | only in functional
      1 /*-------------------------------------------------------------------------
      2  * drawElements Quality Program OpenGL ES 3.1 Module
      3  * -------------------------------------------------
      4  *
      5  * Copyright 2015 The Android Open Source Project
      6  *
      7  * Licensed under the Apache License, Version 2.0 (the "License");
      8  * you may not use this file except in compliance with the License.
      9  * You may obtain a copy of the License at
     10  *
     11  *      http://www.apache.org/licenses/LICENSE-2.0
     12  *
     13  * Unless required by applicable law or agreed to in writing, software
     14  * distributed under the License is distributed on an "AS IS" BASIS,
     15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     16  * See the License for the specific language governing permissions and
     17  * limitations under the License.
     18  *
     19  *//*!
     20  * \file
     21  * \brief Primitive bounding box tests.
     22  *//*--------------------------------------------------------------------*/
     23 
     24 #include "es31fPrimitiveBoundingBoxTests.hpp"
     25 
     26 #include "tcuTestLog.hpp"
     27 #include "tcuRenderTarget.hpp"
     28 #include "tcuSurface.hpp"
     29 #include "tcuTextureUtil.hpp"
     30 #include "tcuVectorUtil.hpp"
     31 #include "gluCallLogWrapper.hpp"
     32 #include "gluContextInfo.hpp"
     33 #include "gluRenderContext.hpp"
     34 #include "gluStrUtil.hpp"
     35 #include "gluShaderProgram.hpp"
     36 #include "gluObjectWrapper.hpp"
     37 #include "gluPixelTransfer.hpp"
     38 #include "glsStateQueryUtil.hpp"
     39 #include "glwFunctions.hpp"
     40 #include "glwEnums.hpp"
     41 #include "deRandom.hpp"
     42 #include "deUniquePtr.hpp"
     43 #include "deStringUtil.hpp"
     44 
     45 #include <vector>
     46 #include <sstream>
     47 #include <algorithm>
     48 
     49 namespace deqp
     50 {
     51 namespace gles31
     52 {
     53 namespace Functional
     54 {
     55 namespace
     56 {
     57 
     58 namespace StateQueryUtil = ::deqp::gls::StateQueryUtil;
     59 
     60 struct BoundingBox
     61 {
     62 	tcu::Vec4 min;
     63 	tcu::Vec4 max;
     64 
     65 	/*--------------------------------------------------------------------*//*!
     66 	 * Get component by index of a 8-component vector constructed by
     67 	 * concatenating 4-component min and max vectors.
     68 	 *//*--------------------------------------------------------------------*/
     69 	float&			getComponentAccess	(int ndx);
     70 	const float&	getComponentAccess	(int ndx) const;
     71 };
     72 
     73 float& BoundingBox::getComponentAccess (int ndx)
     74 {
     75 	DE_ASSERT(ndx >= 0 && ndx < 8);
     76 	if (ndx < 4)
     77 		return min[ndx];
     78 	else
     79 		return max[ndx-4];
     80 }
     81 
     82 const float& BoundingBox::getComponentAccess (int ndx) const
     83 {
     84 	return const_cast<BoundingBox*>(this)->getComponentAccess(ndx);
     85 }
     86 
     87 struct ProjectedBBox
     88 {
     89 	tcu::Vec3	min;
     90 	tcu::Vec3	max;
     91 };
     92 
     93 static ProjectedBBox projectBoundingBox (const BoundingBox& bbox)
     94 {
     95 	const float		wMin	= de::max(0.0f, bbox.min.w()); // clamp to w=0 as extension requires
     96 	const float		wMax	= de::max(0.0f, bbox.max.w());
     97 	ProjectedBBox	retVal;
     98 
     99 	retVal.min = tcu::min(bbox.min.swizzle(0, 1, 2) / wMin,
    100 						  bbox.min.swizzle(0, 1, 2) / wMax);
    101 	retVal.max = tcu::max(bbox.max.swizzle(0, 1, 2) / wMin,
    102 						  bbox.max.swizzle(0, 1, 2) / wMax);
    103 	return retVal;
    104 }
    105 
    106 static tcu::IVec4 getViewportBoundingBoxArea (const ProjectedBBox& bbox, const tcu::IVec2& viewportSize, float size = 0.0f)
    107 {
    108 	tcu::Vec4	vertexBox;
    109 	tcu::IVec4	pixelBox;
    110 
    111 	vertexBox.x() = (bbox.min.x() * 0.5f + 0.5f) * (float)viewportSize.x();
    112 	vertexBox.y() = (bbox.min.y() * 0.5f + 0.5f) * (float)viewportSize.y();
    113 	vertexBox.z() = (bbox.max.x() * 0.5f + 0.5f) * (float)viewportSize.x();
    114 	vertexBox.w() = (bbox.max.y() * 0.5f + 0.5f) * (float)viewportSize.y();
    115 
    116 	pixelBox.x() = deFloorFloatToInt32(vertexBox.x() - size/2.0f);
    117 	pixelBox.y() = deFloorFloatToInt32(vertexBox.y() - size/2.0f);
    118 	pixelBox.z() = deCeilFloatToInt32(vertexBox.z() + size/2.0f);
    119 	pixelBox.w() = deCeilFloatToInt32(vertexBox.w() + size/2.0f);
    120 	return pixelBox;
    121 }
    122 
    123 
    124 class InitialValueCase : public TestCase
    125 {
    126 public:
    127 					InitialValueCase	(Context& context, const char* name, const char* desc);
    128 
    129 	void			init				(void);
    130 	IterateResult	iterate				(void);
    131 };
    132 
    133 InitialValueCase::InitialValueCase (Context& context, const char* name, const char* desc)
    134 	: TestCase(context, name, desc)
    135 {
    136 }
    137 
    138 void InitialValueCase::init (void)
    139 {
    140 	if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box"))
    141 		throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
    142 }
    143 
    144 InitialValueCase::IterateResult InitialValueCase::iterate (void)
    145 {
    146 	StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLfloat[8]>	state;
    147 	glu::CallLogWrapper											gl		(m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
    148 
    149 	gl.enableLogging(true);
    150 
    151 	m_testCtx.getLog()
    152 		<< tcu::TestLog::Message
    153 		<< "Querying GL_PRIMITIVE_BOUNDING_BOX_EXT, expecting (-1, -1, -1, 1) (1, 1, 1, 1)"
    154 		<< tcu::TestLog::EndMessage;
    155 
    156 	gl.glGetFloatv(GL_PRIMITIVE_BOUNDING_BOX_EXT, state);
    157 	GLU_EXPECT_NO_ERROR(gl.glGetError(), "query");
    158 
    159 	if (!state.verifyValidity(m_testCtx))
    160 		return STOP;
    161 
    162 	m_testCtx.getLog()
    163 		<< tcu::TestLog::Message
    164 		<< "Got " << tcu::formatArray(&state[0], &state[8])
    165 		<< tcu::TestLog::EndMessage;
    166 
    167 	if ((state[0] != -1.0f) || (state[1] != -1.0f) || (state[2] != -1.0f) || (state[3] != 1.0f) ||
    168 		(state[4] !=  1.0f) || (state[5] !=  1.0f) || (state[6] !=  1.0f) || (state[7] != 1.0f))
    169 	{
    170 		m_testCtx.getLog()
    171 			<< tcu::TestLog::Message
    172 			<< "Error, unexpected value"
    173 			<< tcu::TestLog::EndMessage;
    174 
    175 		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid initial value");
    176 	}
    177 	else
    178 		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
    179 
    180 	return STOP;
    181 }
    182 
    183 class QueryCase : public TestCase
    184 {
    185 public:
    186 	enum QueryMethod
    187 	{
    188 		QUERY_FLOAT = 0,
    189 		QUERY_BOOLEAN,
    190 		QUERY_INT,
    191 		QUERY_INT64,
    192 
    193 		QUERY_LAST
    194 	};
    195 
    196 						QueryCase	(Context& context, const char* name, const char* desc, QueryMethod method);
    197 
    198 private:
    199 	void				init		(void);
    200 	IterateResult		iterate		(void);
    201 
    202 	bool				verifyState	(glu::CallLogWrapper& gl, const BoundingBox& bbox) const;
    203 
    204 	const QueryMethod	m_method;
    205 };
    206 
    207 QueryCase::QueryCase (Context& context, const char* name, const char* desc, QueryMethod method)
    208 	: TestCase	(context, name, desc)
    209 	, m_method	(method)
    210 {
    211 	DE_ASSERT(method < QUERY_LAST);
    212 }
    213 
    214 void QueryCase::init (void)
    215 {
    216 	if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box"))
    217 		throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
    218 }
    219 
    220 QueryCase::IterateResult QueryCase::iterate (void)
    221 {
    222 	static const BoundingBox fixedCases[] =
    223 	{
    224 		{ tcu::Vec4( 0.0f,  0.0f,  0.0f,  0.0f), tcu::Vec4( 0.0f,  0.0f,  0.0f,  0.0f) },
    225 		{ tcu::Vec4(-0.0f, -0.0f, -0.0f, -0.0f), tcu::Vec4( 0.0f,  0.0f,  0.0f, -0.0f) },
    226 		{ tcu::Vec4( 0.0f,  0.0f,  0.0f,  0.0f), tcu::Vec4( 1.0f,  1.0f,  1.0f, -1.0f) },
    227 		{ tcu::Vec4( 2.0f,  2.0f,  2.0f,  2.0f), tcu::Vec4( 1.5f,  1.5f,  1.5f,  1.0f) },
    228 		{ tcu::Vec4( 1.0f,  1.0f,  1.0f,  1.0f), tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.0f) },
    229 		{ tcu::Vec4( 1.0f,  1.0f,  1.0f,  0.3f), tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.2f) },
    230 	};
    231 
    232 	const int					numRandomCases	= 9;
    233 	glu::CallLogWrapper			gl				(m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
    234 	de::Random					rnd				(0xDE3210);
    235 	std::vector<BoundingBox>	cases;
    236 
    237 	cases.insert(cases.begin(), DE_ARRAY_BEGIN(fixedCases), DE_ARRAY_END(fixedCases));
    238 	for (int ndx = 0; ndx < numRandomCases; ++ndx)
    239 	{
    240 		BoundingBox	boundingBox;
    241 
    242 		// parameter evaluation order is not guaranteed, cannot just do "max = (rand(), rand(), ...)
    243 		for (int coordNdx = 0; coordNdx < 8; ++coordNdx)
    244 			boundingBox.getComponentAccess(coordNdx) = rnd.getFloat(-4.0f, 4.0f);
    245 
    246 		cases.push_back(boundingBox);
    247 	}
    248 
    249 	gl.enableLogging(true);
    250 	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
    251 
    252 	for (int caseNdx = 0; caseNdx < (int)cases.size(); ++caseNdx)
    253 	{
    254 		const tcu::ScopedLogSection	section		(m_testCtx.getLog(), "Iteration", "Iteration " + de::toString(caseNdx+1));
    255 		const BoundingBox&			boundingBox	= cases[caseNdx];
    256 
    257 		gl.glPrimitiveBoundingBox(boundingBox.min.x(), boundingBox.min.y(), boundingBox.min.z(), boundingBox.min.w(),
    258 								  boundingBox.max.x(), boundingBox.max.y(), boundingBox.max.z(), boundingBox.max.w());
    259 
    260 		if (!verifyState(gl, boundingBox))
    261 			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Unexpected query result");
    262 	}
    263 
    264 	return STOP;
    265 }
    266 
    267 bool QueryCase::verifyState (glu::CallLogWrapper& gl, const BoundingBox& bbox) const
    268 {
    269 	switch (m_method)
    270 	{
    271 		case QUERY_FLOAT:
    272 		{
    273 			StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLfloat[8]>	state;
    274 			bool														error = false;
    275 
    276 			gl.glGetFloatv(GL_PRIMITIVE_BOUNDING_BOX_EXT, state);
    277 			GLU_EXPECT_NO_ERROR(gl.glGetError(), "query");
    278 
    279 			if (!state.verifyValidity(m_testCtx))
    280 				return false;
    281 
    282 			m_testCtx.getLog()
    283 					<< tcu::TestLog::Message
    284 					<< "glGetFloatv returned " << tcu::formatArray(&state[0], &state[8])
    285 					<< tcu::TestLog::EndMessage;
    286 
    287 			for (int ndx = 0; ndx < 8; ++ndx)
    288 				if (state[ndx] != bbox.getComponentAccess(ndx))
    289 					error = true;
    290 
    291 			if (error)
    292 			{
    293 				m_testCtx.getLog()
    294 					<< tcu::TestLog::Message
    295 					<< "Error, unexpected value\n"
    296 					<< "Expected ["
    297 					<< bbox.min.x() << ", " << bbox.min.y() << ", " << bbox.min.z() << ", " << bbox.min.w() << ", "
    298 					<< bbox.max.x() << ", " << bbox.max.y() << ", " << bbox.max.z() << ", " << bbox.max.w() << "]"
    299 					<< tcu::TestLog::EndMessage;
    300 				return false;
    301 			}
    302 			return true;
    303 		}
    304 
    305 		case QUERY_INT:
    306 		{
    307 			StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLint[8]>	state;
    308 			bool														error = false;
    309 
    310 			gl.glGetIntegerv(GL_PRIMITIVE_BOUNDING_BOX_EXT, state);
    311 			GLU_EXPECT_NO_ERROR(gl.glGetError(), "query");
    312 
    313 			if (!state.verifyValidity(m_testCtx))
    314 				return false;
    315 
    316 			m_testCtx.getLog()
    317 					<< tcu::TestLog::Message
    318 					<< "glGetIntegerv returned " << tcu::formatArray(&state[0], &state[8])
    319 					<< tcu::TestLog::EndMessage;
    320 
    321 			for (int ndx = 0; ndx < 8; ++ndx)
    322 				if (state[ndx] != StateQueryUtil::roundGLfloatToNearestIntegerHalfDown<glw::GLint>(bbox.getComponentAccess(ndx)) &&
    323 					state[ndx] != StateQueryUtil::roundGLfloatToNearestIntegerHalfUp<glw::GLint>(bbox.getComponentAccess(ndx)))
    324 					error = true;
    325 
    326 			if (error)
    327 			{
    328 				tcu::MessageBuilder builder(&m_testCtx.getLog());
    329 
    330 				builder	<< "Error, unexpected value\n"
    331 						<< "Expected [";
    332 
    333 				for (int ndx = 0; ndx < 8; ++ndx)
    334 				{
    335 					const glw::GLint roundDown	= StateQueryUtil::roundGLfloatToNearestIntegerHalfDown<glw::GLint>(bbox.getComponentAccess(ndx));
    336 					const glw::GLint roundUp	= StateQueryUtil::roundGLfloatToNearestIntegerHalfUp<glw::GLint>(bbox.getComponentAccess(ndx));
    337 
    338 					if (ndx != 0)
    339 						builder << ", ";
    340 
    341 					if (roundDown == roundUp)
    342 						builder << roundDown;
    343 					else
    344 						builder << "{" << roundDown << ", " << roundUp << "}";
    345 				}
    346 
    347 				builder	<< "]"
    348 						<< tcu::TestLog::EndMessage;
    349 				return false;
    350 			}
    351 			return true;
    352 		}
    353 
    354 		case QUERY_INT64:
    355 		{
    356 			StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLint64[8]>	state;
    357 			bool																error = false;
    358 
    359 			gl.glGetInteger64v(GL_PRIMITIVE_BOUNDING_BOX_EXT, state);
    360 			GLU_EXPECT_NO_ERROR(gl.glGetError(), "query");
    361 
    362 			if (!state.verifyValidity(m_testCtx))
    363 				return false;
    364 
    365 			m_testCtx.getLog()
    366 					<< tcu::TestLog::Message
    367 					<< "glGetInteger64v returned " << tcu::formatArray(&state[0], &state[8])
    368 					<< tcu::TestLog::EndMessage;
    369 
    370 			for (int ndx = 0; ndx < 8; ++ndx)
    371 				if (state[ndx] != StateQueryUtil::roundGLfloatToNearestIntegerHalfDown<glw::GLint64>(bbox.getComponentAccess(ndx)) &&
    372 					state[ndx] != StateQueryUtil::roundGLfloatToNearestIntegerHalfUp<glw::GLint64>(bbox.getComponentAccess(ndx)))
    373 					error = true;
    374 
    375 			if (error)
    376 			{
    377 				tcu::MessageBuilder builder(&m_testCtx.getLog());
    378 
    379 				builder	<< "Error, unexpected value\n"
    380 						<< "Expected [";
    381 
    382 				for (int ndx = 0; ndx < 8; ++ndx)
    383 				{
    384 					const glw::GLint64 roundDown	= StateQueryUtil::roundGLfloatToNearestIntegerHalfDown<glw::GLint64>(bbox.getComponentAccess(ndx));
    385 					const glw::GLint64 roundUp		= StateQueryUtil::roundGLfloatToNearestIntegerHalfUp<glw::GLint64>(bbox.getComponentAccess(ndx));
    386 
    387 					if (ndx != 0)
    388 						builder << ", ";
    389 
    390 					if (roundDown == roundUp)
    391 						builder << roundDown;
    392 					else
    393 						builder << "{" << roundDown << ", " << roundUp << "}";
    394 				}
    395 
    396 				builder	<< "]"
    397 						<< tcu::TestLog::EndMessage;
    398 				return false;
    399 			}
    400 			return true;
    401 		}
    402 
    403 		case QUERY_BOOLEAN:
    404 		{
    405 			StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLboolean[8]>	state;
    406 			bool															error = false;
    407 
    408 			gl.glGetBooleanv(GL_PRIMITIVE_BOUNDING_BOX_EXT, state);
    409 			GLU_EXPECT_NO_ERROR(gl.glGetError(), "query");
    410 
    411 			if (!state.verifyValidity(m_testCtx))
    412 				return false;
    413 
    414 			m_testCtx.getLog()
    415 					<< tcu::TestLog::Message
    416 					<< "glGetBooleanv returned ["
    417 					<< glu::getBooleanStr(state[0]) << ", " << glu::getBooleanStr(state[1]) << ", " << glu::getBooleanStr(state[2]) << ", " << glu::getBooleanStr(state[3]) << ", "
    418 					<< glu::getBooleanStr(state[4]) << ", " << glu::getBooleanStr(state[5]) << ", " << glu::getBooleanStr(state[6]) << ", " << glu::getBooleanStr(state[7]) << "]\n"
    419 					<< tcu::TestLog::EndMessage;
    420 
    421 			for (int ndx = 0; ndx < 8; ++ndx)
    422 				if (state[ndx] != ((bbox.getComponentAccess(ndx) != 0.0f) ? (GL_TRUE) : (GL_FALSE)))
    423 					error = true;
    424 
    425 			if (error)
    426 			{
    427 				tcu::MessageBuilder builder(&m_testCtx.getLog());
    428 
    429 				builder	<< "Error, unexpected value\n"
    430 						<< "Expected [";
    431 
    432 				for (int ndx = 0; ndx < 8; ++ndx)
    433 				{
    434 					if (ndx != 0)
    435 						builder << ", ";
    436 
    437 					builder << ((bbox.getComponentAccess(ndx) != 0.0f) ? ("GL_TRUE") : ("GL_FALSE"));
    438 				}
    439 
    440 				builder	<< "]"
    441 						<< tcu::TestLog::EndMessage;
    442 				return false;
    443 			}
    444 			return true;
    445 		}
    446 
    447 		default:
    448 			DE_ASSERT(false);
    449 			return true;
    450 	}
    451 }
    452 
    453 class BBoxRenderCase : public TestCase
    454 {
    455 public:
    456 	enum
    457 	{
    458 		FLAG_RENDERTARGET_DEFAULT	= 1u << 0, //!< render to default renderbuffer
    459 		FLAG_RENDERTARGET_FBO		= 1u << 1, //!< render to framebuffer object
    460 
    461 		FLAG_BBOXSIZE_EQUAL			= 1u << 2, //!< set tight primitive bounding box
    462 		FLAG_BBOXSIZE_LARGER		= 1u << 3, //!< set padded primitive bounding box
    463 		FLAG_BBOXSIZE_SMALLER		= 1u << 4, //!< set too small primitive bounding box
    464 
    465 		FLAG_TESSELLATION			= 1u << 5, //!< use tessellation shader
    466 		FLAG_GEOMETRY				= 1u << 6, //!< use geometry shader
    467 
    468 		FLAG_SET_BBOX_STATE			= 1u << 7, //!< set primitive bounding box using global state
    469 		FLAG_SET_BBOX_OUTPUT		= 1u << 8, //!< set primitive bounding box using tessellation output
    470 		FLAG_PER_PRIMITIVE_BBOX		= 1u << 9, //!< set primitive bounding per primitive
    471 
    472 		FLAGBIT_USER_BIT			= 10u //!< bits N and and up are reserved for subclasses
    473 	};
    474 
    475 									BBoxRenderCase					(Context& context, const char* name, const char* description, int numIterations, deUint32 flags);
    476 									~BBoxRenderCase					(void);
    477 
    478 protected:
    479 	enum RenderTarget
    480 	{
    481 		RENDERTARGET_DEFAULT,
    482 		RENDERTARGET_FBO,
    483 	};
    484 	enum BBoxSize
    485 	{
    486 		BBOXSIZE_EQUAL,
    487 		BBOXSIZE_LARGER,
    488 		BBOXSIZE_SMALLER,
    489 	};
    490 
    491 	enum
    492 	{
    493 		RENDER_TARGET_MIN_SIZE	= 256,
    494 		FBO_SIZE				= 512,
    495 		MIN_VIEWPORT_SIZE		= 256,
    496 		MAX_VIEWPORT_SIZE		= 512,
    497 	};
    498 	DE_STATIC_ASSERT(MIN_VIEWPORT_SIZE <= RENDER_TARGET_MIN_SIZE);
    499 
    500 	enum
    501 	{
    502 		VA_POS_VEC_NDX		= 0,
    503 		VA_COL_VEC_NDX		= 1,
    504 		VA_NUM_ATTRIB_VECS	= 2,
    505 	};
    506 
    507 	enum AABBRoundDirection
    508 	{
    509 		ROUND_INWARDS = 0,
    510 		ROUND_OUTWARDS
    511 	};
    512 
    513 	struct IterationConfig
    514 	{
    515 		tcu::IVec2	viewportPos;
    516 		tcu::IVec2	viewportSize;
    517 		tcu::Vec2	patternPos;		//!< in NDC
    518 		tcu::Vec2	patternSize;	//!< in NDC
    519 		BoundingBox	bbox;
    520 	};
    521 
    522 	virtual void					init							(void);
    523 	virtual void					deinit							(void);
    524 	IterateResult					iterate							(void);
    525 
    526 	virtual std::string				genVertexSource					(void) const = 0;
    527 	virtual std::string				genFragmentSource				(void) const = 0;
    528 	virtual std::string				genTessellationControlSource	(void) const = 0;
    529 	virtual std::string				genTessellationEvaluationSource	(void) const = 0;
    530 	virtual std::string				genGeometrySource				(void) const = 0;
    531 
    532 	virtual IterationConfig			generateConfig					(int iteration, const tcu::IVec2& renderTargetSize) const = 0;
    533 	virtual void					getAttributeData				(std::vector<tcu::Vec4>& data) const = 0;
    534 	virtual void					renderTestPattern				(const IterationConfig& config) = 0;
    535 	virtual void					verifyRenderResult				(const IterationConfig& config) = 0;
    536 
    537 	IterationConfig					generateRandomConfig			(int seed, const tcu::IVec2& renderTargetSize) const;
    538 	tcu::IVec4						getViewportPatternArea			(const tcu::Vec2& patternPos, const tcu::Vec2& patternSize, const tcu::IVec2& viewportSize, AABBRoundDirection roundDir) const;
    539 
    540 	void							setupRender						(const IterationConfig& config);
    541 
    542 	enum ShaderFunction
    543 	{
    544 		SHADER_FUNC_MIRROR_X,
    545 		SHADER_FUNC_MIRROR_Y,
    546 		SHADER_FUNC_INSIDE_BBOX,
    547 	};
    548 
    549 	const char*						genShaderFunction				(ShaderFunction func) const;
    550 
    551 	const RenderTarget				m_renderTarget;
    552 	const BBoxSize					m_bboxSize;
    553 	const bool						m_hasTessellationStage;
    554 	const bool						m_hasGeometryStage;
    555 	const bool						m_useGlobalState;
    556 	const bool						m_calcPerPrimitiveBBox;
    557 	const int						m_numIterations;
    558 
    559 	de::MovePtr<glu::ShaderProgram>	m_program;
    560 	de::MovePtr<glu::Buffer>		m_vbo;
    561 	de::MovePtr<glu::Framebuffer>	m_fbo;
    562 
    563 private:
    564 	std::vector<IterationConfig>	m_iterationConfigs;
    565 	int								m_iteration;
    566 };
    567 
    568 BBoxRenderCase::BBoxRenderCase (Context& context, const char* name, const char* description, int numIterations, deUint32 flags)
    569 	: TestCase					(context, name, description)
    570 	, m_renderTarget			((flags & FLAG_RENDERTARGET_DEFAULT) ? (RENDERTARGET_DEFAULT) : (RENDERTARGET_FBO))
    571 	, m_bboxSize				((flags & FLAG_BBOXSIZE_EQUAL) ? (BBOXSIZE_EQUAL) : (flags & FLAG_BBOXSIZE_SMALLER) ? (BBOXSIZE_SMALLER) : (BBOXSIZE_LARGER))
    572 	, m_hasTessellationStage	((flags & FLAG_TESSELLATION) != 0)
    573 	, m_hasGeometryStage		((flags & FLAG_GEOMETRY) != 0)
    574 	, m_useGlobalState			((flags & FLAG_SET_BBOX_STATE) != 0)
    575 	, m_calcPerPrimitiveBBox	((flags & FLAG_PER_PRIMITIVE_BBOX) != 0)
    576 	, m_numIterations			(numIterations)
    577 	, m_iteration				(0)
    578 {
    579 	// validate flags
    580 	DE_ASSERT((((m_renderTarget == RENDERTARGET_DEFAULT)	?	(FLAG_RENDERTARGET_DEFAULT)	: (0)) |
    581 			   ((m_renderTarget == RENDERTARGET_FBO)		?	(FLAG_RENDERTARGET_FBO)		: (0)) |
    582 			   ((m_bboxSize == BBOXSIZE_EQUAL)				?	(FLAG_BBOXSIZE_EQUAL)		: (0)) |
    583 			   ((m_bboxSize == BBOXSIZE_LARGER)				?	(FLAG_BBOXSIZE_LARGER)		: (0)) |
    584 			   ((m_bboxSize == BBOXSIZE_SMALLER)			?	(FLAG_BBOXSIZE_SMALLER)		: (0)) |
    585 			   ((m_hasTessellationStage)					?	(FLAG_TESSELLATION)			: (0)) |
    586 			   ((m_hasGeometryStage)						?	(FLAG_GEOMETRY)				: (0)) |
    587 			   ((m_useGlobalState)							?	(FLAG_SET_BBOX_STATE)		: (0)) |
    588 			   ((!m_useGlobalState)							?	(FLAG_SET_BBOX_OUTPUT)		: (0)) |
    589 			   ((m_calcPerPrimitiveBBox)					?	(FLAG_PER_PRIMITIVE_BBOX)	: (0))) == (flags & ((1u << FLAGBIT_USER_BIT) - 1)));
    590 
    591 	DE_ASSERT(m_useGlobalState || m_hasTessellationStage); // using non-global state requires tessellation
    592 
    593 	if (m_calcPerPrimitiveBBox)
    594 	{
    595 		DE_ASSERT(!m_useGlobalState); // per-primitive test requires per-primitive (non-global) state
    596 		DE_ASSERT(m_bboxSize == BBOXSIZE_EQUAL); // smaller is hard to verify, larger not interesting
    597 	}
    598 }
    599 
    600 BBoxRenderCase::~BBoxRenderCase (void)
    601 {
    602 	deinit();
    603 }
    604 
    605 void BBoxRenderCase::init (void)
    606 {
    607 	const glw::Functions&	gl					= m_context.getRenderContext().getFunctions();
    608 	const tcu::IVec2		renderTargetSize	= (m_renderTarget == RENDERTARGET_DEFAULT) ?
    609 													(tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) :
    610 													(tcu::IVec2(FBO_SIZE, FBO_SIZE));
    611 
    612 	// requirements
    613 	if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box"))
    614 		throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
    615 	if (m_hasTessellationStage && !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader"))
    616 		throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader extension");
    617 	if (m_hasGeometryStage && !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader"))
    618 		throw tcu::NotSupportedError("Test requires GL_EXT_geometry_shader extension");
    619 	if (m_renderTarget == RENDERTARGET_DEFAULT && (renderTargetSize.x() < RENDER_TARGET_MIN_SIZE || renderTargetSize.y() < RENDER_TARGET_MIN_SIZE))
    620 		throw tcu::NotSupportedError(std::string() + "Test requires " + de::toString<int>(RENDER_TARGET_MIN_SIZE) + "x" + de::toString<int>(RENDER_TARGET_MIN_SIZE) + " default framebuffer");
    621 
    622 	// log case specifics
    623 	m_testCtx.getLog()
    624 		<< tcu::TestLog::Message
    625 		<< "Setting primitive bounding box "
    626 			<< ((m_calcPerPrimitiveBBox)         ? ("to exactly cover each generated primitive")
    627 			  : (m_bboxSize == BBOXSIZE_EQUAL)   ? ("to exactly cover rendered grid")
    628 			  : (m_bboxSize == BBOXSIZE_LARGER)  ? ("to cover the grid and include some padding")
    629 			  : (m_bboxSize == BBOXSIZE_SMALLER) ? ("to cover only a subset of the grid")
    630 			  : (DE_NULL))
    631 			<< ".\n"
    632 		<< "Rendering with vertex"
    633 			<< ((m_hasTessellationStage) ? ("-tessellation{ctrl,eval}") : (""))
    634 			<< ((m_hasGeometryStage) ? ("-geometry") : (""))
    635 			<< "-fragment program.\n"
    636 		<< "Set bounding box using "
    637 			<< ((m_useGlobalState) ? ("PRIMITIVE_BOUNDING_BOX_EXT state") : ("gl_BoundingBoxEXT output"))
    638 			<< "\n"
    639 		<< "Verifying rendering results are valid within the bounding box."
    640 		<< tcu::TestLog::EndMessage;
    641 
    642 	// resources
    643 
    644 	{
    645 		glu::ProgramSources sources;
    646 		sources << glu::VertexSource(genVertexSource());
    647 		sources << glu::FragmentSource(genFragmentSource());
    648 
    649 		if (m_hasTessellationStage)
    650 			sources << glu::TessellationControlSource(genTessellationControlSource())
    651 					<< glu::TessellationEvaluationSource(genTessellationEvaluationSource());
    652 		if (m_hasGeometryStage)
    653 			sources << glu::GeometrySource(genGeometrySource());
    654 
    655 		m_program = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(), sources));
    656 		GLU_EXPECT_NO_ERROR(gl.getError(), "build program");
    657 
    658 		{
    659 			const tcu::ScopedLogSection section(m_testCtx.getLog(), "ShaderProgram", "Shader program");
    660 			m_testCtx.getLog() << *m_program;
    661 		}
    662 
    663 		if (!m_program->isOk())
    664 			throw tcu::TestError("failed to build program");
    665 	}
    666 
    667 	if (m_renderTarget == RENDERTARGET_FBO)
    668 	{
    669 		glu::Texture colorAttachment(m_context.getRenderContext());
    670 
    671 		gl.bindTexture(GL_TEXTURE_2D, *colorAttachment);
    672 		gl.texStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, FBO_SIZE, FBO_SIZE);
    673 		GLU_EXPECT_NO_ERROR(gl.getError(), "gen tex");
    674 
    675 		m_fbo = de::MovePtr<glu::Framebuffer>(new glu::Framebuffer(m_context.getRenderContext()));
    676 		gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, **m_fbo);
    677 		gl.framebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, *colorAttachment, 0);
    678 		GLU_EXPECT_NO_ERROR(gl.getError(), "attach");
    679 
    680 		// unbind to prevent texture name deletion from removing it from current fbo attachments
    681 		gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
    682 	}
    683 
    684 	{
    685 		std::vector<tcu::Vec4> data;
    686 
    687 		getAttributeData(data);
    688 
    689 		m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext()));
    690 		gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
    691 		gl.bufferData(GL_ARRAY_BUFFER, (int)(data.size() * sizeof(tcu::Vec4)), &data[0], GL_STATIC_DRAW);
    692 		GLU_EXPECT_NO_ERROR(gl.getError(), "create vbo");
    693 	}
    694 
    695 	// Iterations
    696 	for (int iterationNdx = 0; iterationNdx < m_numIterations; ++iterationNdx)
    697 		m_iterationConfigs.push_back(generateConfig(iterationNdx, renderTargetSize));
    698 }
    699 
    700 void BBoxRenderCase::deinit (void)
    701 {
    702 	m_program.clear();
    703 	m_vbo.clear();
    704 	m_fbo.clear();
    705 }
    706 
    707 BBoxRenderCase::IterateResult BBoxRenderCase::iterate (void)
    708 {
    709 	const tcu::ScopedLogSection	section		(m_testCtx.getLog(),
    710 											 std::string() + "Iteration" + de::toString((int)m_iteration),
    711 											 std::string() + "Iteration " + de::toString((int)m_iteration+1) + "/" + de::toString((int)m_iterationConfigs.size()));
    712 	const IterationConfig&		config		= m_iterationConfigs[m_iteration];
    713 
    714 	// default
    715 	if (m_iteration == 0)
    716 		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
    717 
    718 	renderTestPattern(config);
    719 	verifyRenderResult(config);
    720 
    721 	if (++m_iteration < (int)m_iterationConfigs.size())
    722 		return CONTINUE;
    723 
    724 	return STOP;
    725 }
    726 
    727 BBoxRenderCase::IterationConfig BBoxRenderCase::generateRandomConfig (int seed, const tcu::IVec2& renderTargetSize) const
    728 {
    729 	de::Random		rnd		(seed);
    730 	IterationConfig	config;
    731 
    732 	// viewport config
    733 	config.viewportSize.x()	= rnd.getInt(MIN_VIEWPORT_SIZE, de::min<int>(renderTargetSize.x(), MAX_VIEWPORT_SIZE));
    734 	config.viewportSize.y()	= rnd.getInt(MIN_VIEWPORT_SIZE, de::min<int>(renderTargetSize.y(), MAX_VIEWPORT_SIZE));
    735 	config.viewportPos.x()	= rnd.getInt(0, renderTargetSize.x() - config.viewportSize.x());
    736 	config.viewportPos.y()	= rnd.getInt(0, renderTargetSize.y() - config.viewportSize.y());
    737 
    738 	// pattern location inside viewport
    739 	config.patternSize.x()	= rnd.getFloat(0.4f, 1.4f);
    740 	config.patternSize.y()	= rnd.getFloat(0.4f, 1.4f);
    741 	config.patternPos.x()	= rnd.getFloat(-1.0f, 1.0f - config.patternSize.x());
    742 	config.patternPos.y()	= rnd.getFloat(-1.0f, 1.0f - config.patternSize.y());
    743 
    744 	// accurate bounding box
    745 	config.bbox.min			= tcu::Vec4(config.patternPos.x(), config.patternPos.y(), 0.0f, 1.0f);
    746 	config.bbox.max			= tcu::Vec4(config.patternPos.x() + config.patternSize.x(), config.patternPos.y() + config.patternSize.y(), 0.0f, 1.0f);
    747 
    748 	if (m_bboxSize == BBOXSIZE_LARGER)
    749 	{
    750 		// increase bbox size
    751 		config.bbox.min.x() -= rnd.getFloat() * 0.5f;
    752 		config.bbox.min.y() -= rnd.getFloat() * 0.5f;
    753 		config.bbox.min.z() -= rnd.getFloat() * 0.5f;
    754 
    755 		config.bbox.max.x() += rnd.getFloat() * 0.5f;
    756 		config.bbox.max.y() += rnd.getFloat() * 0.5f;
    757 		config.bbox.max.z() += rnd.getFloat() * 0.5f;
    758 	}
    759 	else if (m_bboxSize == BBOXSIZE_SMALLER)
    760 	{
    761 		// reduce bbox size
    762 		config.bbox.min.x() += rnd.getFloat() * 0.4f * config.patternSize.x();
    763 		config.bbox.min.y() += rnd.getFloat() * 0.4f * config.patternSize.y();
    764 
    765 		config.bbox.max.x() -= rnd.getFloat() * 0.4f * config.patternSize.x();
    766 		config.bbox.max.y() -= rnd.getFloat() * 0.4f * config.patternSize.y();
    767 	}
    768 
    769 	return config;
    770 }
    771 
    772 tcu::IVec4 BBoxRenderCase::getViewportPatternArea (const tcu::Vec2& patternPos, const tcu::Vec2& patternSize, const tcu::IVec2& viewportSize, AABBRoundDirection roundDir) const
    773 {
    774 	const float	halfPixel	= 0.5f;
    775 	tcu::Vec4	vertexBox;
    776 	tcu::IVec4	pixelBox;
    777 
    778 	vertexBox.x() = (patternPos.x() * 0.5f + 0.5f) * (float)viewportSize.x();
    779 	vertexBox.y() = (patternPos.y() * 0.5f + 0.5f) * (float)viewportSize.y();
    780 	vertexBox.z() = ((patternPos.x() + patternSize.x()) * 0.5f + 0.5f) * (float)viewportSize.x();
    781 	vertexBox.w() = ((patternPos.y() + patternSize.y()) * 0.5f + 0.5f) * (float)viewportSize.y();
    782 
    783 	if (roundDir == ROUND_INWARDS)
    784 	{
    785 		pixelBox.x() = (int)deFloatCeil(vertexBox.x()+halfPixel);
    786 		pixelBox.y() = (int)deFloatCeil(vertexBox.y()+halfPixel);
    787 		pixelBox.z() = (int)deFloatFloor(vertexBox.z()-halfPixel);
    788 		pixelBox.w() = (int)deFloatFloor(vertexBox.w()-halfPixel);
    789 	}
    790 	else
    791 	{
    792 		pixelBox.x() = (int)deFloatFloor(vertexBox.x()-halfPixel);
    793 		pixelBox.y() = (int)deFloatFloor(vertexBox.y()-halfPixel);
    794 		pixelBox.z() = (int)deFloatCeil(vertexBox.z()+halfPixel);
    795 		pixelBox.w() = (int)deFloatCeil(vertexBox.w()+halfPixel);
    796 	}
    797 
    798 	return pixelBox;
    799 }
    800 
    801 void BBoxRenderCase::setupRender (const IterationConfig& config)
    802 {
    803 	const glw::Functions&	gl					= m_context.getRenderContext().getFunctions();
    804 	const glw::GLint		posLocation			= gl.getAttribLocation(m_program->getProgram(), "a_position");
    805 	const glw::GLint		colLocation			= gl.getAttribLocation(m_program->getProgram(), "a_color");
    806 	const glw::GLint		posScaleLocation	= gl.getUniformLocation(m_program->getProgram(), "u_posScale");
    807 
    808 	TCU_CHECK(posLocation != -1);
    809 	TCU_CHECK(colLocation != -1);
    810 	TCU_CHECK(posScaleLocation != -1);
    811 
    812 	m_testCtx.getLog()
    813 		<< tcu::TestLog::Message
    814 		<< "Setting viewport to ("
    815 			<< "x: " << config.viewportPos.x() << ", "
    816 			<< "y: " << config.viewportPos.y() << ", "
    817 			<< "w: " << config.viewportSize.x() << ", "
    818 			<< "h: " << config.viewportSize.y() << ")\n"
    819 		<< "Vertex coordinates are in range:\n"
    820 			<< "\tx: [" << config.patternPos.x() << ", " << (config.patternPos.x() + config.patternSize.x()) << "]\n"
    821 			<< "\ty: [" << config.patternPos.y() << ", " << (config.patternPos.y() + config.patternSize.y()) << "]\n"
    822 		<< tcu::TestLog::EndMessage;
    823 
    824 	if (!m_calcPerPrimitiveBBox)
    825 		m_testCtx.getLog()
    826 			<< tcu::TestLog::Message
    827 			<< "Setting primitive bounding box to:\n"
    828 				<< "\t" << config.bbox.min << "\n"
    829 				<< "\t" << config.bbox.max << "\n"
    830 			<< tcu::TestLog::EndMessage;
    831 
    832 	if (m_useGlobalState)
    833 		gl.primitiveBoundingBox(config.bbox.min.x(), config.bbox.min.y(), config.bbox.min.z(), config.bbox.min.w(),
    834 								config.bbox.max.x(), config.bbox.max.y(), config.bbox.max.z(), config.bbox.max.w());
    835 	else
    836 		// state is overriden by the tessellation output, set bbox to invisible area to imitiate dirty state left by application
    837 		gl.primitiveBoundingBox(-2.0f, -2.0f, 0.0f, 1.0f,
    838 								-1.7f, -1.7f, 0.0f, 1.0f);
    839 
    840 	if (m_fbo)
    841 		gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, **m_fbo);
    842 
    843 	gl.viewport(config.viewportPos.x(), config.viewportPos.y(), config.viewportSize.x(), config.viewportSize.y());
    844 	gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
    845 	gl.clear(GL_COLOR_BUFFER_BIT);
    846 
    847 	gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
    848 	gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, (int)(VA_NUM_ATTRIB_VECS * sizeof(float[4])), (const float*)DE_NULL + 4 * VA_POS_VEC_NDX);
    849 	gl.vertexAttribPointer(colLocation, 4, GL_FLOAT, GL_FALSE, (int)(VA_NUM_ATTRIB_VECS * sizeof(float[4])), (const float*)DE_NULL + 4 * VA_COL_VEC_NDX);
    850 	gl.enableVertexAttribArray(posLocation);
    851 	gl.enableVertexAttribArray(colLocation);
    852 	gl.useProgram(m_program->getProgram());
    853 	gl.uniform4f(posScaleLocation, config.patternPos.x(), config.patternPos.y(), config.patternSize.x(), config.patternSize.y());
    854 
    855 	{
    856 		const glw::GLint bboxMinPos = gl.getUniformLocation(m_program->getProgram(), "u_primitiveBBoxMin");
    857 		const glw::GLint bboxMaxPos = gl.getUniformLocation(m_program->getProgram(), "u_primitiveBBoxMax");
    858 
    859 		gl.uniform4f(bboxMinPos, config.bbox.min.x(), config.bbox.min.y(), config.bbox.min.z(), config.bbox.min.w());
    860 		gl.uniform4f(bboxMaxPos, config.bbox.max.x(), config.bbox.max.y(), config.bbox.max.z(), config.bbox.max.w());
    861 	}
    862 
    863 	gl.uniform2i(gl.getUniformLocation(m_program->getProgram(), "u_viewportPos"), config.viewportPos.x(), config.viewportPos.y());
    864 	gl.uniform2i(gl.getUniformLocation(m_program->getProgram(), "u_viewportSize"), config.viewportSize.x(), config.viewportSize.y());
    865 
    866 	GLU_EXPECT_NO_ERROR(gl.getError(), "setup");
    867 }
    868 
    869 const char* BBoxRenderCase::genShaderFunction (ShaderFunction func) const
    870 {
    871 	switch (func)
    872 	{
    873 		case SHADER_FUNC_MIRROR_X:
    874 			return	"vec4 mirrorX(in highp vec4 p)\n"
    875 					"{\n"
    876 					"	highp vec2 patternOffset = u_posScale.xy;\n"
    877 					"	highp vec2 patternScale = u_posScale.zw;\n"
    878 					"	highp vec2 patternCenter = patternOffset + patternScale * 0.5;\n"
    879 					"	return vec4(2.0 * patternCenter.x - p.x, p.y, p.z, p.w);\n"
    880 					"}\n";
    881 
    882 		case SHADER_FUNC_MIRROR_Y:
    883 			return	"vec4 mirrorY(in highp vec4 p)\n"
    884 					"{\n"
    885 					"	highp vec2 patternOffset = u_posScale.xy;\n"
    886 					"	highp vec2 patternScale = u_posScale.zw;\n"
    887 					"	highp vec2 patternCenter = patternOffset + patternScale * 0.5;\n"
    888 					"	return vec4(p.x, 2.0 * patternCenter.y - p.y, p.z, p.w);\n"
    889 					"}\n";
    890 
    891 		case SHADER_FUNC_INSIDE_BBOX:
    892 			return	"uniform highp ivec2 u_viewportPos;\n"
    893 					"uniform highp ivec2 u_viewportSize;\n"
    894 					"flat in highp float v_bbox_expansionSize;\n"
    895 					"flat in highp vec3 v_bbox_clipMin;\n"
    896 					"flat in highp vec3 v_bbox_clipMax;\n"
    897 					"\n"
    898 					"bool fragmentInsideTheBBox(in highp float depth)\n"
    899 					"{\n"
    900 					"	highp vec4 wc = vec4(floor((v_bbox_clipMin.x * 0.5 + 0.5) * float(u_viewportSize.x) - v_bbox_expansionSize/2.0),\n"
    901 					"	                     floor((v_bbox_clipMin.y * 0.5 + 0.5) * float(u_viewportSize.y) - v_bbox_expansionSize/2.0),\n"
    902 					"	                     ceil((v_bbox_clipMax.x * 0.5 + 0.5) * float(u_viewportSize.x) + v_bbox_expansionSize/2.0),\n"
    903 					"	                     ceil((v_bbox_clipMax.y * 0.5 + 0.5) * float(u_viewportSize.y) + v_bbox_expansionSize/2.0));\n"
    904 					"	if (gl_FragCoord.x < float(u_viewportPos.x) + wc.x || gl_FragCoord.x > float(u_viewportPos.x) + wc.z ||\n"
    905 					"	    gl_FragCoord.y < float(u_viewportPos.y) + wc.y || gl_FragCoord.y > float(u_viewportPos.y) + wc.w)\n"
    906 					"	    return false;\n"
    907 					"	const highp float dEpsilon = 0.001;\n"
    908 					"	if (depth*2.0-1.0 < v_bbox_clipMin.z - dEpsilon || depth*2.0-1.0 > v_bbox_clipMax.z + dEpsilon)\n"
    909 					"	    return false;\n"
    910 					"	return true;\n"
    911 					"}\n";
    912 		default:
    913 			DE_ASSERT(false);
    914 			return "";
    915 	}
    916 }
    917 
    918 class GridRenderCase : public BBoxRenderCase
    919 {
    920 public:
    921 					GridRenderCase					(Context& context, const char* name, const char* description, deUint32 flags);
    922 					~GridRenderCase					(void);
    923 
    924 private:
    925 	void			init							(void);
    926 
    927 	std::string		genVertexSource					(void) const;
    928 	std::string		genFragmentSource				(void) const;
    929 	std::string		genTessellationControlSource	(void) const;
    930 	std::string		genTessellationEvaluationSource	(void) const;
    931 	std::string		genGeometrySource				(void) const;
    932 
    933 	IterationConfig	generateConfig					(int iteration, const tcu::IVec2& renderTargetSize) const;
    934 	void			getAttributeData				(std::vector<tcu::Vec4>& data) const;
    935 	void			renderTestPattern				(const IterationConfig& config);
    936 	void			verifyRenderResult				(const IterationConfig& config);
    937 
    938 	const int		m_gridSize;
    939 };
    940 
    941 GridRenderCase::GridRenderCase (Context& context, const char* name, const char* description, deUint32 flags)
    942 	: BBoxRenderCase	(context, name, description, 12, flags)
    943 	, m_gridSize		(24)
    944 {
    945 }
    946 
    947 GridRenderCase::~GridRenderCase (void)
    948 {
    949 }
    950 
    951 void GridRenderCase::init (void)
    952 {
    953 	m_testCtx.getLog()
    954 		<< tcu::TestLog::Message
    955 		<< "Rendering yellow-green grid to " << ((m_renderTarget == RENDERTARGET_DEFAULT) ? ("default frame buffer") : ("fbo")) << ".\n"
    956 		<< "Grid cells are in random order, varying grid size and location for each iteration.\n"
    957 		<< "Marking all discardable fragments (fragments outside the bounding box) with a fully saturated blue channel."
    958 		<< tcu::TestLog::EndMessage;
    959 
    960 	BBoxRenderCase::init();
    961 }
    962 
    963 std::string GridRenderCase::genVertexSource (void) const
    964 {
    965 	std::ostringstream	buf;
    966 
    967 	buf <<	"#version 310 es\n"
    968 			"in highp vec4 a_position;\n"
    969 			"in highp vec4 a_color;\n"
    970 			"out highp vec4 vtx_color;\n"
    971 			"uniform highp vec4 u_posScale;\n"
    972 			"\n";
    973 	if (!m_hasTessellationStage)
    974 	{
    975 		DE_ASSERT(m_useGlobalState);
    976 		buf <<	"uniform highp vec4 u_primitiveBBoxMin;\n"
    977 				"uniform highp vec4 u_primitiveBBoxMax;\n"
    978 				"\n"
    979 				"flat out highp float v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize;\n"
    980 				"flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n"
    981 				"flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n"
    982 				"\n";
    983 	}
    984 
    985 	buf <<	"void main()\n"
    986 			"{\n"
    987 			"	highp vec2 patternOffset = u_posScale.xy;\n"
    988 			"	highp vec2 patternScale = u_posScale.zw;\n"
    989 			"	gl_Position = vec4(a_position.xy * patternScale + patternOffset, a_position.z, a_position.w);\n"
    990 			"	vtx_color = a_color;\n";
    991 
    992 	if (!m_hasTessellationStage)
    993 	{
    994 		DE_ASSERT(m_useGlobalState);
    995 		buf <<	"\n"
    996 				"	v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize = 0.0;\n"
    997 				"	v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin =\n"
    998 				"	    min(vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMin.w,\n"
    999 				"	        vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMax.w);\n"
   1000 				"	v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax =\n"
   1001 				"	    min(vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMin.w,\n"
   1002 				"	        vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMax.w);\n";
   1003 	}
   1004 
   1005 	buf<<	"}\n";
   1006 
   1007 	return buf.str();
   1008 }
   1009 
   1010 std::string GridRenderCase::genFragmentSource (void) const
   1011 {
   1012 	const char* const	colorInputName = (m_hasGeometryStage) ? ("geo_color") : (m_hasTessellationStage) ? ("tess_color") : ("vtx_color");
   1013 	std::ostringstream	buf;
   1014 
   1015 	buf <<	"#version 310 es\n"
   1016 			"in mediump vec4 " << colorInputName << ";\n"
   1017 			"layout(location = 0) out mediump vec4 o_color;\n"
   1018 		<<	genShaderFunction(SHADER_FUNC_INSIDE_BBOX)
   1019 		<<	"\n"
   1020 			"void main()\n"
   1021 			"{\n"
   1022 			"	mediump vec4 baseColor = " << colorInputName << ";\n"
   1023 			"	mediump float blueChannel;\n"
   1024 			"	if (fragmentInsideTheBBox(gl_FragCoord.z))\n"
   1025 			"		blueChannel = 0.0;\n"
   1026 			"	else\n"
   1027 			"		blueChannel = 1.0;\n"
   1028 			"	o_color = vec4(baseColor.r, baseColor.g, blueChannel, baseColor.a);\n"
   1029 			"}\n";
   1030 
   1031 	return buf.str();
   1032 }
   1033 
   1034 std::string GridRenderCase::genTessellationControlSource (void) const
   1035 {
   1036 	std::ostringstream	buf;
   1037 
   1038 	buf <<	"#version 310 es\n"
   1039 			"#extension GL_EXT_tessellation_shader : require\n"
   1040 			"#extension GL_EXT_primitive_bounding_box : require\n"
   1041 			"layout(vertices=3) out;\n"
   1042 			"\n"
   1043 			"in highp vec4 vtx_color[];\n"
   1044 			"out highp vec4 tess_ctrl_color[];\n"
   1045 			"uniform highp float u_tessellationLevel;\n"
   1046 			"uniform highp vec4 u_posScale;\n";
   1047 
   1048 	if (!m_calcPerPrimitiveBBox)
   1049 	{
   1050 		buf <<	"uniform highp vec4 u_primitiveBBoxMin;\n"
   1051 				"uniform highp vec4 u_primitiveBBoxMax;\n";
   1052 	}
   1053 
   1054 	buf <<	"patch out highp float vp_bbox_expansionSize;\n"
   1055 			"patch out highp vec3 vp_bbox_clipMin;\n"
   1056 			"patch out highp vec3 vp_bbox_clipMax;\n";
   1057 
   1058 	if (m_calcPerPrimitiveBBox)
   1059 	{
   1060 		buf <<	"\n";
   1061 		if (m_hasGeometryStage)
   1062 			buf << genShaderFunction(SHADER_FUNC_MIRROR_X);
   1063 		buf << genShaderFunction(SHADER_FUNC_MIRROR_Y);
   1064 
   1065 		buf <<	"vec4 transformVec(in highp vec4 p)\n"
   1066 				"{\n"
   1067 				"	return " << ((m_hasGeometryStage) ? ("mirrorX(mirrorY(p))") : ("mirrorY(p)")) << ";\n"
   1068 				"}\n";
   1069 	}
   1070 
   1071 	buf <<	"\n"
   1072 			"void main()\n"
   1073 			"{\n"
   1074 			"	// convert to nonsensical coordinates, just in case\n"
   1075 			"	gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position.wzxy;\n"
   1076 			"	tess_ctrl_color[gl_InvocationID] = vtx_color[gl_InvocationID];\n"
   1077 			"\n"
   1078 			"	gl_TessLevelOuter[0] = u_tessellationLevel;\n"
   1079 			"	gl_TessLevelOuter[1] = u_tessellationLevel;\n"
   1080 			"	gl_TessLevelOuter[2] = u_tessellationLevel;\n"
   1081 			"	gl_TessLevelInner[0] = u_tessellationLevel;\n";
   1082 
   1083 	if (m_calcPerPrimitiveBBox)
   1084 	{
   1085 		buf <<	"\n"
   1086 				"	highp vec4 bboxMin = min(min(transformVec(gl_in[0].gl_Position),\n"
   1087 				"	                             transformVec(gl_in[1].gl_Position)),\n"
   1088 				"	                         transformVec(gl_in[2].gl_Position));\n"
   1089 				"	highp vec4 bboxMax = max(max(transformVec(gl_in[0].gl_Position),\n"
   1090 				"	                             transformVec(gl_in[1].gl_Position)),\n"
   1091 				"	                         transformVec(gl_in[2].gl_Position));\n";
   1092 	}
   1093 	else
   1094 	{
   1095 		buf <<	"\n"
   1096 				"	highp vec4 bboxMin = u_primitiveBBoxMin;\n"
   1097 				"	highp vec4 bboxMax = u_primitiveBBoxMax;\n";
   1098 	}
   1099 
   1100 	if (!m_useGlobalState)
   1101 		buf <<	"\n"
   1102 				"	gl_BoundingBoxEXT[0] = bboxMin;\n"
   1103 				"	gl_BoundingBoxEXT[1] = bboxMax;\n";
   1104 
   1105 	buf <<	"	vp_bbox_expansionSize = 0.0;\n"
   1106 			"	vp_bbox_clipMin = min(vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMin.w,\n"
   1107 			"	                      vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMax.w);\n"
   1108 			"	vp_bbox_clipMax = max(vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMin.w,\n"
   1109 			"	                      vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMax.w);\n"
   1110 			"}\n";
   1111 
   1112 	return buf.str();
   1113 }
   1114 
   1115 std::string GridRenderCase::genTessellationEvaluationSource (void) const
   1116 {
   1117 	std::ostringstream	buf;
   1118 
   1119 	buf <<	"#version 310 es\n"
   1120 			"#extension GL_EXT_tessellation_shader : require\n"
   1121 			"#extension GL_EXT_gpu_shader5 : require\n"
   1122 			"layout(triangles) in;\n"
   1123 			"\n"
   1124 			"in highp vec4 tess_ctrl_color[];\n"
   1125 			"out highp vec4 tess_color;\n"
   1126 			"uniform highp vec4 u_posScale;\n"
   1127 			"patch in highp float vp_bbox_expansionSize;\n"
   1128 			"patch in highp vec3 vp_bbox_clipMin;\n"
   1129 			"patch in highp vec3 vp_bbox_clipMax;\n"
   1130 			"flat out highp float v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize;\n"
   1131 			"flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n"
   1132 			"flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n"
   1133 			"\n"
   1134 			"precise gl_Position;\n"
   1135 			"\n"
   1136 		<<	genShaderFunction(SHADER_FUNC_MIRROR_Y)
   1137 		<<	"void main()\n"
   1138 			"{\n"
   1139 			"	// non-trivial tessellation evaluation shader, convert from nonsensical coords, flip vertically\n"
   1140 			"	gl_Position = mirrorY(gl_TessCoord.x * gl_in[0].gl_Position.zwyx +\n"
   1141 			"	                      gl_TessCoord.y * gl_in[1].gl_Position.zwyx +\n"
   1142 			"	                      gl_TessCoord.z * gl_in[2].gl_Position.zwyx);\n"
   1143 			"	tess_color = tess_ctrl_color[0];\n"
   1144 			"	v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize = vp_bbox_expansionSize;\n"
   1145 			"	v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin = vp_bbox_clipMin;\n"
   1146 			"	v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax = vp_bbox_clipMax;\n"
   1147 			"}\n";
   1148 
   1149 	return buf.str();
   1150 }
   1151 
   1152 std::string GridRenderCase::genGeometrySource (void) const
   1153 {
   1154 	const char* const	colorInputName = (m_hasTessellationStage) ? ("tess_color") : ("vtx_color");
   1155 	std::ostringstream	buf;
   1156 
   1157 	buf <<	"#version 310 es\n"
   1158 			"#extension GL_EXT_geometry_shader : require\n"
   1159 			"layout(triangles) in;\n"
   1160 			"layout(max_vertices=9, triangle_strip) out;\n"
   1161 			"\n"
   1162 			"in highp vec4 " << colorInputName << "[3];\n"
   1163 			"out highp vec4 geo_color;\n"
   1164 			"uniform highp vec4 u_posScale;\n"
   1165 			"\n"
   1166 			"flat in highp float v_geo_bbox_expansionSize[3];\n"
   1167 			"flat in highp vec3 v_geo_bbox_clipMin[3];\n"
   1168 			"flat in highp vec3 v_geo_bbox_clipMax[3];\n"
   1169 			"flat out highp vec3 v_bbox_clipMin;\n"
   1170 			"flat out highp vec3 v_bbox_clipMax;\n"
   1171 			"flat out highp float v_bbox_expansionSize;\n"
   1172 		<<	genShaderFunction(SHADER_FUNC_MIRROR_X)
   1173 		<<	"\n"
   1174 			"void setVisualizationVaryings()\n"
   1175 			"{\n"
   1176 			"	v_bbox_expansionSize = v_geo_bbox_expansionSize[0];\n"
   1177 			"	v_bbox_clipMin = v_geo_bbox_clipMin[0];\n"
   1178 			"	v_bbox_clipMax = v_geo_bbox_clipMax[0];\n"
   1179 			"}\n"
   1180 			"void main()\n"
   1181 			"{\n"
   1182 			"	// Non-trivial geometry shader: 1-to-3 amplification, mirror horizontally\n"
   1183 			"	highp vec4 p0 = mirrorX(gl_in[0].gl_Position);\n"
   1184 			"	highp vec4 p1 = mirrorX(gl_in[1].gl_Position);\n"
   1185 			"	highp vec4 p2 = mirrorX(gl_in[2].gl_Position);\n"
   1186 			"	highp vec4 pCentroid = vec4((p0.xyz + p1.xyz + p2.xyz) / 3.0, 1.0);\n"
   1187 			"	highp vec4 triangleColor = " << colorInputName << "[0];\n"
   1188 			"\n"
   1189 			"	gl_Position = p0; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n"
   1190 			"	gl_Position = p1; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n"
   1191 			"	gl_Position = pCentroid; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n"
   1192 			"	EndPrimitive();\n"
   1193 			"\n"
   1194 			"	gl_Position = p1; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n"
   1195 			"	gl_Position = p2; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n"
   1196 			"	gl_Position = pCentroid; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n"
   1197 			"	EndPrimitive();\n"
   1198 			"\n"
   1199 			"	gl_Position = p2; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n"
   1200 			"	gl_Position = p0; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n"
   1201 			"	gl_Position = pCentroid; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n"
   1202 			"	EndPrimitive();\n"
   1203 			"}\n";
   1204 
   1205 	return buf.str();
   1206 }
   1207 
   1208 GridRenderCase::IterationConfig GridRenderCase::generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const
   1209 {
   1210 	return generateRandomConfig(0xDEDEDEu * (deUint32)iteration, renderTargetSize);
   1211 }
   1212 
   1213 void GridRenderCase::getAttributeData (std::vector<tcu::Vec4>& data) const
   1214 {
   1215 	const tcu::Vec4		green				(0.0f, 1.0f, 0.0f, 1.0f);
   1216 	const tcu::Vec4		yellow				(1.0f, 1.0f, 0.0f, 1.0f);
   1217 	std::vector<int>	cellOrder			(m_gridSize * m_gridSize);
   1218 	de::Random			rnd					(0xDE56789);
   1219 
   1220 	// generate grid with cells in random order
   1221 	for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
   1222 		cellOrder[ndx] = ndx;
   1223 	rnd.shuffle(cellOrder.begin(), cellOrder.end());
   1224 
   1225 	data.resize(m_gridSize * m_gridSize * 6 * 2);
   1226 	for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
   1227 	{
   1228 		const int			cellNdx		= cellOrder[ndx];
   1229 		const int			cellX		= cellNdx % m_gridSize;
   1230 		const int			cellY		= cellNdx / m_gridSize;
   1231 		const tcu::Vec4&	cellColor	= ((cellX+cellY)%2 == 0) ? (green) : (yellow);
   1232 
   1233 		data[(ndx * 6 + 0) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(cellX+0) / float(m_gridSize), float(cellY+0) / float(m_gridSize), 0.0f, 1.0f);
   1234 		data[(ndx * 6 + 0) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor;
   1235 		data[(ndx * 6 + 1) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(cellX+1) / float(m_gridSize), float(cellY+1) / float(m_gridSize), 0.0f, 1.0f);
   1236 		data[(ndx * 6 + 1) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor;
   1237 		data[(ndx * 6 + 2) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(cellX+0) / float(m_gridSize), float(cellY+1) / float(m_gridSize), 0.0f, 1.0f);
   1238 		data[(ndx * 6 + 2) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor;
   1239 		data[(ndx * 6 + 3) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(cellX+0) / float(m_gridSize), float(cellY+0) / float(m_gridSize), 0.0f, 1.0f);
   1240 		data[(ndx * 6 + 3) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor;
   1241 		data[(ndx * 6 + 4) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(cellX+1) / float(m_gridSize), float(cellY+0) / float(m_gridSize), 0.0f, 1.0f);
   1242 		data[(ndx * 6 + 4) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor;
   1243 		data[(ndx * 6 + 5) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(cellX+1) / float(m_gridSize), float(cellY+1) / float(m_gridSize), 0.0f, 1.0f);
   1244 		data[(ndx * 6 + 5) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor;
   1245 	}
   1246 }
   1247 
   1248 void GridRenderCase::renderTestPattern (const IterationConfig& config)
   1249 {
   1250 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
   1251 
   1252 	setupRender(config);
   1253 
   1254 	if (m_hasTessellationStage)
   1255 	{
   1256 		const glw::GLint	tessLevelPos	= gl.getUniformLocation(m_program->getProgram(), "u_tessellationLevel");
   1257 		const glw::GLfloat	tessLevel		= 2.8f; // will be rounded up
   1258 
   1259 		TCU_CHECK(tessLevelPos != -1);
   1260 
   1261 		m_testCtx.getLog() << tcu::TestLog::Message << "u_tessellationLevel = " << tessLevel << tcu::TestLog::EndMessage;
   1262 
   1263 		gl.uniform1f(tessLevelPos, tessLevel);
   1264 		gl.patchParameteri(GL_PATCH_VERTICES, 3);
   1265 		GLU_EXPECT_NO_ERROR(gl.getError(), "patch param");
   1266 	}
   1267 
   1268 	m_testCtx.getLog() << tcu::TestLog::Message << "Rendering grid." << tcu::TestLog::EndMessage;
   1269 
   1270 	gl.drawArrays((m_hasTessellationStage) ? (GL_PATCHES) : (GL_TRIANGLES), 0, m_gridSize * m_gridSize * 6);
   1271 	GLU_EXPECT_NO_ERROR(gl.getError(), "draw");
   1272 }
   1273 
   1274 void GridRenderCase::verifyRenderResult (const IterationConfig& config)
   1275 {
   1276 	const glw::Functions&	gl						= m_context.getRenderContext().getFunctions();
   1277 	const ProjectedBBox		projectedBBox			= projectBoundingBox(config.bbox);
   1278 	const tcu::IVec4		viewportBBoxArea		= getViewportBoundingBoxArea(projectedBBox, config.viewportSize);
   1279 	const tcu::IVec4		viewportGridOuterArea	= getViewportPatternArea(config.patternPos, config.patternSize, config.viewportSize, ROUND_OUTWARDS);
   1280 	const tcu::IVec4		viewportGridInnerArea	= getViewportPatternArea(config.patternPos, config.patternSize, config.viewportSize, ROUND_INWARDS);
   1281 	tcu::Surface			viewportSurface			(config.viewportSize.x(), config.viewportSize.y());
   1282 	tcu::Surface			errorMask				(config.viewportSize.x(), config.viewportSize.y());
   1283 	bool					anyError				= false;
   1284 
   1285 	if (!m_calcPerPrimitiveBBox)
   1286 		m_testCtx.getLog()
   1287 			<< tcu::TestLog::Message
   1288 			<< "Projected bounding box: (clip space)\n"
   1289 				<< "\tx: [" << projectedBBox.min.x() << "," << projectedBBox.max.x() << "]\n"
   1290 				<< "\ty: [" << projectedBBox.min.y() << "," << projectedBBox.max.y() << "]\n"
   1291 				<< "\tz: [" << projectedBBox.min.z() << "," << projectedBBox.max.z() << "]\n"
   1292 			<< "In viewport coordinates:\n"
   1293 				<< "\tx: [" << viewportBBoxArea.x() << ", " << viewportBBoxArea.z() << "]\n"
   1294 				<< "\ty: [" << viewportBBoxArea.y() << ", " << viewportBBoxArea.w() << "]\n"
   1295 			<< "Verifying render results within the bounding box.\n"
   1296 			<< tcu::TestLog::EndMessage;
   1297 	else
   1298 		m_testCtx.getLog()
   1299 			<< tcu::TestLog::Message
   1300 			<< "Verifying render result."
   1301 			<< tcu::TestLog::EndMessage;
   1302 
   1303 	if (m_fbo)
   1304 		gl.bindFramebuffer(GL_READ_FRAMEBUFFER, **m_fbo);
   1305 	glu::readPixels(m_context.getRenderContext(), config.viewportPos.x(), config.viewportPos.y(), viewportSurface.getAccess());
   1306 
   1307 	tcu::clear(errorMask.getAccess(), tcu::IVec4(0,0,0,255));
   1308 
   1309 	for (int y = de::max(viewportBBoxArea.y(), 0); y < de::min(viewportBBoxArea.w(), config.viewportSize.y()); ++y)
   1310 	for (int x = de::max(viewportBBoxArea.x(), 0); x < de::min(viewportBBoxArea.z(), config.viewportSize.x()); ++x)
   1311 	{
   1312 		const tcu::RGBA	pixel		= viewportSurface.getPixel(x, y);
   1313 		const bool		outsideGrid	= x < viewportGridOuterArea.x() ||
   1314 									  y < viewportGridOuterArea.y() ||
   1315 									  x > viewportGridOuterArea.z() ||
   1316 									  y > viewportGridOuterArea.w();
   1317 		const bool		insideGrid	= x > viewportGridInnerArea.x() &&
   1318 									  y > viewportGridInnerArea.y() &&
   1319 									  x < viewportGridInnerArea.z() &&
   1320 									  y < viewportGridInnerArea.w();
   1321 
   1322 		bool			error		= false;
   1323 
   1324 		if (outsideGrid)
   1325 		{
   1326 			// expect black
   1327 			if (pixel.getRed() != 0 || pixel.getGreen() != 0 || pixel.getBlue() != 0)
   1328 				error = true;
   1329 		}
   1330 
   1331 		else if (insideGrid)
   1332 		{
   1333 			// expect green, yellow or a combination of these
   1334 			if (pixel.getGreen() != 255 || pixel.getBlue() != 0)
   1335 				error = true;
   1336 		}
   1337 		else
   1338 		{
   1339 			// boundary, allow anything
   1340 		}
   1341 
   1342 		if (error)
   1343 		{
   1344 			errorMask.setPixel(x, y, tcu::RGBA::red());
   1345 			anyError = true;
   1346 		}
   1347 	}
   1348 
   1349 	if (anyError)
   1350 	{
   1351 		m_testCtx.getLog()
   1352 			<< tcu::TestLog::Message
   1353 			<< "Image verification failed."
   1354 			<< tcu::TestLog::EndMessage
   1355 			<< tcu::TestLog::ImageSet("Images", "Image verification")
   1356 			<< tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess())
   1357 			<< tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess())
   1358 			<< tcu::TestLog::EndImageSet;
   1359 
   1360 		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
   1361 	}
   1362 	else
   1363 	{
   1364 		m_testCtx.getLog()
   1365 			<< tcu::TestLog::Message
   1366 			<< "Result image ok."
   1367 			<< tcu::TestLog::EndMessage
   1368 			<< tcu::TestLog::ImageSet("Images", "Image verification")
   1369 			<< tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess())
   1370 			<< tcu::TestLog::EndImageSet;
   1371 	}
   1372 }
   1373 
   1374 class LineRenderCase : public BBoxRenderCase
   1375 {
   1376 public:
   1377 	enum
   1378 	{
   1379 		LINEFLAG_WIDE = 1u << FLAGBIT_USER_BIT,	//!< use wide lines
   1380 	};
   1381 
   1382 					LineRenderCase					(Context& context, const char* name, const char* description, deUint32 flags);
   1383 					~LineRenderCase					(void);
   1384 
   1385 private:
   1386 	enum
   1387 	{
   1388 		GREEN_COMPONENT_NDX = 1,
   1389 		BLUE_COMPONENT_NDX = 2,
   1390 
   1391 		SCAN_ROW_COMPONENT_NDX = GREEN_COMPONENT_NDX, // \note: scans are orthogonal to the line
   1392 		SCAN_COL_COMPONENT_NDX = BLUE_COMPONENT_NDX,
   1393 	};
   1394 
   1395 	enum QueryDirection
   1396 	{
   1397 		DIRECTION_HORIZONTAL = 0,
   1398 		DIRECTION_VERTICAL,
   1399 	};
   1400 
   1401 	enum ScanResult
   1402 	{
   1403 		SCANRESULT_NUM_LINES_OK_BIT		= (1 << 0),
   1404 		SCANRESULT_LINE_WIDTH_OK_BIT	= (1 << 1),
   1405 	};
   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 
   1433 LineRenderCase::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 
   1441 LineRenderCase::~LineRenderCase (void)
   1442 {
   1443 }
   1444 
   1445 void 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 
   1467 std::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 
   1512 std::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 
   1536 std::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 
   1614 std::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 
   1646 std::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 
   1696 LineRenderCase::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 
   1716 void 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 
   1753 void 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 
   1788 void 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 
   1918 tcu::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 
   1950 deUint8 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 
   1959 deUint8 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 
   1968 bool 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 
   1998 tcu::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 
   2055 bool 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 
   2150 void 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 
   2162 class PointRenderCase : public BBoxRenderCase
   2163 {
   2164 public:
   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 
   2179 private:
   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 
   2213 PointRenderCase::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 
   2220 PointRenderCase::~PointRenderCase (void)
   2221 {
   2222 }
   2223 
   2224 void 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 
   2257 void PointRenderCase::deinit (void)
   2258 {
   2259 	// clear data
   2260 	m_attribData = std::vector<tcu::Vec4>();
   2261 
   2262 	// deinit parent
   2263 	BBoxRenderCase::deinit();
   2264 }
   2265 
   2266 std::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 
   2316 std::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 
   2340 std::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 
   2427 std::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 
   2468 std::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 
   2525 PointRenderCase::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 
   2549 void 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 
   2581 void PointRenderCase::getAttributeData (std::vector<tcu::Vec4>& data) const
   2582 {
   2583 	data = m_attribData;
   2584 }
   2585 
   2586 void 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 
   2616 void 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 
   2683 struct 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 
   2696 void 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 
   2775 bool 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 
   2837 bool 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 
   2867 bool 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 
   2927 bool 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 
   3052 tcu::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 
   3084 class BlitFboCase : public TestCase
   3085 {
   3086 public:
   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 
   3098 private:
   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 
   3133 BlitFboCase::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 
   3143 BlitFboCase::~BlitFboCase (void)
   3144 {
   3145 	deinit();
   3146 }
   3147 
   3148 void 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 
   3320 void 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 
   3330 BlitFboCase::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.primitiveBoundingBox(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 
   3374 bool 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 
   3433 void 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 
   3452 class DepthDrawCase : public TestCase
   3453 {
   3454 public:
   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 
   3480 private:
   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 
   3517 DepthDrawCase::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 
   3530 DepthDrawCase::~DepthDrawCase (void)
   3531 {
   3532 	deinit();
   3533 }
   3534 
   3535 void 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 
   3617 void DepthDrawCase::deinit (void)
   3618 {
   3619 	m_program.clear();
   3620 	m_vbo.clear();
   3621 }
   3622 
   3623 DepthDrawCase::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.primitiveBoundingBox(-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 
   3693 std::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 
   3731 std::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 
   3759 std::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 
   3806 std::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 
   3845 void 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 
   3882 bool 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 
   3927 class ClearCase : public TestCase
   3928 {
   3929 public:
   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 
   3941 private:
   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 
   3975 ClearCase::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 
   3986 ClearCase::~ClearCase (void)
   3987 {
   3988 	deinit();
   3989 }
   3990 
   3991 void 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 
   4022 void 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 
   4031 ClearCase::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 
   4074 void 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 
   4113 void 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 
   4149 void 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.primitiveBoundingBox(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 
   4251 bool 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 
   4287 bool 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 
   4322 static 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 
   4333 static 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 
   4342 static 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 
   4359 std::string ClearCase::genVertexSource (void) const
   4360 {
   4361 	return	s_yellowishPosOnlyVertexSource;
   4362 }
   4363 
   4364 std::string ClearCase::genFragmentSource (void) const
   4365 {
   4366 	return s_basicColorFragmentSource;
   4367 }
   4368 
   4369 std::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 
   4406 std::string ClearCase::genTessellationEvaluationSource (void) const
   4407 {
   4408 	return s_basicColorTessEvalSource;
   4409 }
   4410 
   4411 class ViewportCallOrderCase : public TestCase
   4412 {
   4413 public:
   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 
   4425 private:
   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 
   4446 ViewportCallOrderCase::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 
   4454 ViewportCallOrderCase::~ViewportCallOrderCase (void)
   4455 {
   4456 	deinit();
   4457 }
   4458 
   4459 void 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 	if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader"))
   4465 		throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader extension");
   4466 
   4467 	m_testCtx.getLog()
   4468 		<< tcu::TestLog::Message
   4469 		<< "Testing call order of state setting functions have no effect on the rendering.\n"
   4470 		<< "Setting viewport and bounding box in the following order:\n"
   4471 			<< ((m_callOrder == VIEWPORT_FIRST)
   4472 				? ("\tFirst viewport with glViewport function.\n")
   4473 				: ("\tFirst bounding box with glPrimitiveBoundingBoxEXT function.\n"))
   4474 			<< ((m_callOrder == VIEWPORT_FIRST)
   4475 				? ("\tThen bounding box with glPrimitiveBoundingBoxEXT function.\n")
   4476 				: ("\tThen viewport with glViewport function.\n"))
   4477 		<< "Verifying rendering result."
   4478 		<< tcu::TestLog::EndMessage;
   4479 
   4480 	// resources
   4481 	genVbo();
   4482 	genProgram();
   4483 }
   4484 
   4485 void ViewportCallOrderCase::deinit (void)
   4486 {
   4487 	m_vbo.clear();
   4488 	m_program.clear();
   4489 }
   4490 
   4491 ViewportCallOrderCase::IterateResult ViewportCallOrderCase::iterate (void)
   4492 {
   4493 	const glw::Functions&	gl				= m_context.getRenderContext().getFunctions();
   4494 	const tcu::IVec2		viewportSize	= tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight());
   4495 	const glw::GLint		posLocation		= gl.getAttribLocation(m_program->getProgram(), "a_position");
   4496 	tcu::Surface			resultSurface	(viewportSize.x(), viewportSize.y());
   4497 
   4498 	gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
   4499 	gl.clear(GL_COLOR_BUFFER_BIT);
   4500 
   4501 	// set state
   4502 	for (int orderNdx = 0; orderNdx < 2; ++orderNdx)
   4503 	{
   4504 		if ((orderNdx == 0 && m_callOrder == VIEWPORT_FIRST) ||
   4505 			(orderNdx == 1 && m_callOrder == BBOX_FIRST))
   4506 		{
   4507 			m_testCtx.getLog()
   4508 				<< tcu::TestLog::Message
   4509 				<< "Setting viewport to cover the left half of the render target.\n"
   4510 				<< "\t(0, 0, " << (viewportSize.x()/2) << ", " << viewportSize.y() << ")"
   4511 				<< tcu::TestLog::EndMessage;
   4512 
   4513 			gl.viewport(0, 0, viewportSize.x()/2, viewportSize.y());
   4514 		}
   4515 		else
   4516 		{
   4517 			m_testCtx.getLog()
   4518 				<< tcu::TestLog::Message
   4519 				<< "Setting bounding box to cover the right half of the clip space.\n"
   4520 				<< "\t(0.0, -1.0, -1.0, 1.0) .. (1.0, 1.0, 1.0f, 1.0)"
   4521 				<< tcu::TestLog::EndMessage;
   4522 
   4523 			gl.primitiveBoundingBox(0.0f, -1.0f, -1.0f, 1.0f,
   4524 									1.0f,  1.0f,  1.0f, 1.0f);
   4525 		}
   4526 	}
   4527 
   4528 	m_testCtx.getLog()
   4529 		<< tcu::TestLog::Message
   4530 		<< "Rendering mesh covering the right half of the clip space."
   4531 		<< tcu::TestLog::EndMessage;
   4532 
   4533 	gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
   4534 	gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, sizeof(float[4]), (const float*)DE_NULL);
   4535 	gl.enableVertexAttribArray(posLocation);
   4536 	gl.useProgram(m_program->getProgram());
   4537 	gl.patchParameteri(GL_PATCH_VERTICES, 3);
   4538 	gl.drawArrays(GL_PATCHES, 0, m_numVertices);
   4539 	GLU_EXPECT_NO_ERROR(gl.getError(), "post-draw");
   4540 
   4541 	m_testCtx.getLog()
   4542 		<< tcu::TestLog::Message
   4543 		<< "Verifying image"
   4544 		<< tcu::TestLog::EndMessage;
   4545 	glu::readPixels(m_context.getRenderContext(), 0, 0, resultSurface.getAccess());
   4546 
   4547 	if (!verifyImage(resultSurface.getAccess()))
   4548 		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
   4549 	else
   4550 	{
   4551 		m_testCtx.getLog()
   4552 			<< tcu::TestLog::Message
   4553 			<< "Result ok."
   4554 			<< tcu::TestLog::EndMessage
   4555 			<< tcu::TestLog::ImageSet("Images", "Image verification")
   4556 			<< tcu::TestLog::Image("Result", "Result", resultSurface.getAccess())
   4557 			<< tcu::TestLog::EndImageSet;
   4558 
   4559 		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
   4560 	}
   4561 	return STOP;
   4562 }
   4563 
   4564 void ViewportCallOrderCase::genVbo (void)
   4565 {
   4566 	const int				gridSize	= 6;
   4567 	const glw::Functions&	gl			= m_context.getRenderContext().getFunctions();
   4568 	std::vector<tcu::Vec4>	data		(gridSize * gridSize * 2 * 3);
   4569 	std::vector<int>		cellOrder	(gridSize * gridSize * 2);
   4570 	de::Random				rnd			(0x55443322);
   4571 
   4572 	// generate grid with triangles in random order
   4573 	for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
   4574 		cellOrder[ndx] = ndx;
   4575 	rnd.shuffle(cellOrder.begin(), cellOrder.end());
   4576 
   4577 	// generate grid filling the right half of the clip space: (x: 0.0, y: -1.0) .. (x: 1.0, y: 1.0)
   4578 	for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
   4579 	{
   4580 		const int			cellNdx		= cellOrder[ndx];
   4581 		const bool			cellSide	= ((cellNdx % 2) == 0);
   4582 		const int			cellX		= (cellNdx / 2) % gridSize;
   4583 		const int			cellY		= (cellNdx / 2) / gridSize;
   4584 
   4585 		if (cellSide)
   4586 		{
   4587 			data[ndx * 3 + 0] = tcu::Vec4(float(cellX+0) / float(gridSize), (float(cellY+0) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
   4588 			data[ndx * 3 + 1] = tcu::Vec4(float(cellX+1) / float(gridSize), (float(cellY+1) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
   4589 			data[ndx * 3 + 2] = tcu::Vec4(float(cellX+0) / float(gridSize), (float(cellY+1) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
   4590 		}
   4591 		else
   4592 		{
   4593 			data[ndx * 3 + 0] = tcu::Vec4(float(cellX+0) / float(gridSize), (float(cellY+0) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
   4594 			data[ndx * 3 + 1] = tcu::Vec4(float(cellX+1) / float(gridSize), (float(cellY+0) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
   4595 			data[ndx * 3 + 2] = tcu::Vec4(float(cellX+1) / float(gridSize), (float(cellY+1) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
   4596 		}
   4597 	}
   4598 
   4599 	m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext()));
   4600 	gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
   4601 	gl.bufferData(GL_ARRAY_BUFFER, (int)(data.size() * sizeof(tcu::Vec4)), &data[0], GL_STATIC_DRAW);
   4602 	GLU_EXPECT_NO_ERROR(gl.getError(), "create vbo");
   4603 
   4604 	m_numVertices = (int)data.size();
   4605 }
   4606 
   4607 void ViewportCallOrderCase::genProgram (void)
   4608 {
   4609 	m_program = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(),
   4610 																	   glu::ProgramSources()
   4611 																			<< glu::VertexSource(genVertexSource())
   4612 																			<< glu::FragmentSource(genFragmentSource())
   4613 																			<< glu::TessellationControlSource(genTessellationControlSource())
   4614 																			<< glu::TessellationEvaluationSource(genTessellationEvaluationSource())));
   4615 
   4616 	m_testCtx.getLog()
   4617 		<< tcu::TestLog::Section("Program", "Shader program")
   4618 		<< *m_program
   4619 		<< tcu::TestLog::EndSection;
   4620 
   4621 	if (!m_program->isOk())
   4622 		throw tcu::TestError("shader build failed");
   4623 }
   4624 
   4625 bool ViewportCallOrderCase::verifyImage (const tcu::PixelBufferAccess& result)
   4626 {
   4627 	const tcu::IVec2	insideBorder	(deCeilFloatToInt32(0.25f * (float)result.getWidth()) + 1, deFloorFloatToInt32(0.5f * (float)result.getWidth()) - 1);
   4628 	const tcu::IVec2	outsideBorder	(deFloorFloatToInt32(0.25f * (float)result.getWidth()) - 1, deCeilFloatToInt32(0.5f * (float)result.getWidth()) + 1);
   4629 	tcu::Surface		errorMask		(result.getWidth(), result.getHeight());
   4630 	bool				anyError		= false;
   4631 
   4632 	tcu::clear(errorMask.getAccess(), tcu::RGBA::green().toIVec());
   4633 
   4634 	for (int y = 0; y < result.getHeight(); ++y)
   4635 	for (int x = 0; x < result.getWidth(); ++x)
   4636 	{
   4637 		const tcu::IVec4	pixel			= result.getPixelInt(x, y);
   4638 		const bool			insideMeshArea	= x >= insideBorder.x() && x <= insideBorder.x();
   4639 		const bool			outsideMeshArea = x <= outsideBorder.x() && x >= outsideBorder.x();
   4640 
   4641 		// inside mesh, allow green, yellow and any shade between
   4642 		// outside mesh, allow background (black) only
   4643 		// in the border area, allow anything
   4644 		if ((insideMeshArea && (pixel[1] != 255 || pixel[2] != 0)) ||
   4645 			(outsideMeshArea && (pixel[0] != 0 || pixel[1] != 0 || pixel[2] != 0)))
   4646 		{
   4647 			errorMask.setPixel(x, y, tcu::RGBA::red());
   4648 			anyError = true;
   4649 		}
   4650 	}
   4651 
   4652 	if (anyError)
   4653 	{
   4654 		m_testCtx.getLog()
   4655 			<< tcu::TestLog::Message
   4656 			<< "Image verification failed."
   4657 			<< tcu::TestLog::EndMessage
   4658 			<< tcu::TestLog::ImageSet("Images", "Image verification")
   4659 			<< tcu::TestLog::Image("ResultImage", "Result image", result)
   4660 			<< tcu::TestLog::Image("ErrorMask", "Error mask", errorMask)
   4661 			<< tcu::TestLog::EndImageSet;
   4662 	}
   4663 
   4664 	return !anyError;
   4665 }
   4666 
   4667 std::string ViewportCallOrderCase::genVertexSource (void) const
   4668 {
   4669 	return	s_yellowishPosOnlyVertexSource;
   4670 }
   4671 
   4672 std::string ViewportCallOrderCase::genFragmentSource (void) const
   4673 {
   4674 	return s_basicColorFragmentSource;
   4675 }
   4676 
   4677 std::string ViewportCallOrderCase::genTessellationControlSource (void) const
   4678 {
   4679 	return	"#version 310 es\n"
   4680 			"#extension GL_EXT_tessellation_shader : require\n"
   4681 			"layout(vertices=3) out;\n"
   4682 			"in highp vec4 v_vertex_color[];\n"
   4683 			"out highp vec4 v_tess_eval_color[];\n"
   4684 			"void main()\n"
   4685 			"{\n"
   4686 			"	gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
   4687 			"	v_tess_eval_color[gl_InvocationID] = v_vertex_color[gl_InvocationID];\n"
   4688 			"	gl_TessLevelOuter[0] = 2.8;\n"
   4689 			"	gl_TessLevelOuter[1] = 2.8;\n"
   4690 			"	gl_TessLevelOuter[2] = 2.8;\n"
   4691 			"	gl_TessLevelInner[0] = 2.8;\n"
   4692 			"}\n";
   4693 }
   4694 
   4695 std::string ViewportCallOrderCase::genTessellationEvaluationSource (void) const
   4696 {
   4697 	return s_basicColorTessEvalSource;
   4698 }
   4699 
   4700 } // anonymous
   4701 
   4702 PrimitiveBoundingBoxTests::PrimitiveBoundingBoxTests (Context& context)
   4703 	: TestCaseGroup(context, "primitive_bounding_box", "Tests for EXT_primitive_bounding_box")
   4704 {
   4705 }
   4706 
   4707 PrimitiveBoundingBoxTests::~PrimitiveBoundingBoxTests (void)
   4708 {
   4709 }
   4710 
   4711 void PrimitiveBoundingBoxTests::init (void)
   4712 {
   4713 	static const struct
   4714 	{
   4715 		const char*	name;
   4716 		const char*	description;
   4717 		deUint32	methodFlags;
   4718 	} stateSetMethods[] =
   4719 	{
   4720 		{
   4721 			"global_state",
   4722 			"Set bounding box using PRIMITIVE_BOUNDING_BOX_EXT state",
   4723 			BBoxRenderCase::FLAG_SET_BBOX_STATE,
   4724 		},
   4725 		{
   4726 			"tessellation_set_per_draw",
   4727 			"Set bounding box using gl_BoundingBoxEXT, use same value for all primitives",
   4728 			BBoxRenderCase::FLAG_SET_BBOX_OUTPUT,
   4729 		},
   4730 		{
   4731 			"tessellation_set_per_primitive",
   4732 			"Set bounding box using gl_BoundingBoxEXT, use per-primitive bounding box",
   4733 			BBoxRenderCase::FLAG_SET_BBOX_OUTPUT | BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
   4734 		},
   4735 	};
   4736 	static const struct
   4737 	{
   4738 		const char*	name;
   4739 		const char*	description;
   4740 		deUint32	stageFlags;
   4741 	} pipelineConfigs[] =
   4742 	{
   4743 		{
   4744 			"vertex_fragment",
   4745 			"Render with vertex-fragment program",
   4746 			0u
   4747 		},
   4748 		{
   4749 			"vertex_tessellation_fragment",
   4750 			"Render with vertex-tessellation{ctrl,eval}-fragment program",
   4751 			BBoxRenderCase::FLAG_TESSELLATION
   4752 		},
   4753 		{
   4754 			"vertex_geometry_fragment",
   4755 			"Render with vertex-tessellation{ctrl,eval}-geometry-fragment program",
   4756 			BBoxRenderCase::FLAG_GEOMETRY
   4757 		},
   4758 		{
   4759 			"vertex_tessellation_geometry_fragment",
   4760 			"Render with vertex-geometry-fragment program",
   4761 			BBoxRenderCase::FLAG_TESSELLATION | BBoxRenderCase::FLAG_GEOMETRY
   4762 		},
   4763 	};
   4764 	static const struct
   4765 	{
   4766 		const char*	name;
   4767 		const char*	description;
   4768 		deUint32	flags;
   4769 		deUint32	invalidFlags;
   4770 		deUint32	requiredFlags;
   4771 	} usageConfigs[] =
   4772 	{
   4773 		{
   4774 			"default_framebuffer_bbox_equal",
   4775 			"Render to default framebuffer, set tight bounding box",
   4776 			BBoxRenderCase::FLAG_RENDERTARGET_DEFAULT | BBoxRenderCase::FLAG_BBOXSIZE_EQUAL,
   4777 			BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
   4778 			0
   4779 		},
   4780 		{
   4781 			"default_framebuffer_bbox_larger",
   4782 			"Render to default framebuffer, set padded bounding box",
   4783 			BBoxRenderCase::FLAG_RENDERTARGET_DEFAULT | BBoxRenderCase::FLAG_BBOXSIZE_LARGER,
   4784 			BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
   4785 			0
   4786 		},
   4787 		{
   4788 			"default_framebuffer_bbox_smaller",
   4789 			"Render to default framebuffer, set too small bounding box",
   4790 			BBoxRenderCase::FLAG_RENDERTARGET_DEFAULT | BBoxRenderCase::FLAG_BBOXSIZE_SMALLER,
   4791 			BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
   4792 			0
   4793 		},
   4794 		{
   4795 			"fbo_bbox_equal",
   4796 			"Render to texture, set tight bounding box",
   4797 			BBoxRenderCase::FLAG_RENDERTARGET_FBO | BBoxRenderCase::FLAG_BBOXSIZE_EQUAL,
   4798 			BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
   4799 			0
   4800 		},
   4801 		{
   4802 			"fbo_bbox_larger",
   4803 			"Render to texture, set padded bounding box",
   4804 			BBoxRenderCase::FLAG_RENDERTARGET_FBO | BBoxRenderCase::FLAG_BBOXSIZE_LARGER,
   4805 			BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
   4806 			0
   4807 		},
   4808 		{
   4809 			"fbo_bbox_smaller",
   4810 			"Render to texture, set too small bounding box",
   4811 			BBoxRenderCase::FLAG_RENDERTARGET_FBO | BBoxRenderCase::FLAG_BBOXSIZE_SMALLER,
   4812 			BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
   4813 			0
   4814 		},
   4815 		{
   4816 			"default_framebuffer",
   4817 			"Render to default framebuffer, set tight bounding box",
   4818 			BBoxRenderCase::FLAG_RENDERTARGET_DEFAULT | BBoxRenderCase::FLAG_BBOXSIZE_EQUAL,
   4819 			0,
   4820 			BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX
   4821 		},
   4822 		{
   4823 			"fbo",
   4824 			"Render to texture, set tight bounding box",
   4825 			BBoxRenderCase::FLAG_RENDERTARGET_FBO | BBoxRenderCase::FLAG_BBOXSIZE_EQUAL,
   4826 			0,
   4827 			BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX
   4828 		},
   4829 	};
   4830 	enum PrimitiveRenderType
   4831 	{
   4832 		TYPE_TRIANGLE,
   4833 		TYPE_LINE,
   4834 		TYPE_POINT,
   4835 	};
   4836 	const struct
   4837 	{
   4838 		const char*			name;
   4839 		const char*			description;
   4840 		PrimitiveRenderType	type;
   4841 		deUint32			flags;
   4842 	} primitiveTypes[] =
   4843 	{
   4844 		{
   4845 			"triangles",
   4846 			"Triangle render tests",
   4847 			TYPE_TRIANGLE,
   4848 			0
   4849 		},
   4850 		{
   4851 			"lines",
   4852 			"Line render tests",
   4853 			TYPE_LINE,
   4854 			0
   4855 		},
   4856 		{
   4857 			"points",
   4858 			"Point render tests",
   4859 			TYPE_POINT,
   4860 			0
   4861 		},
   4862 		{
   4863 			"wide_lines",
   4864 			"Wide line render tests",
   4865 			TYPE_LINE,
   4866 			LineRenderCase::LINEFLAG_WIDE
   4867 		},
   4868 		{
   4869 			"wide_points",
   4870 			"Wide point render tests",
   4871 			TYPE_POINT,
   4872 			PointRenderCase::POINTFLAG_WIDE
   4873 		},
   4874 	};
   4875 
   4876 	// .state_query
   4877 	{
   4878 		tcu::TestCaseGroup* const stateQueryGroup = new tcu::TestCaseGroup(m_testCtx, "state_query", "State queries");
   4879 		addChild(stateQueryGroup);
   4880 
   4881 		stateQueryGroup->addChild(new InitialValueCase	(m_context,	"initial_value",	"Initial value case"));
   4882 		stateQueryGroup->addChild(new QueryCase			(m_context,	"getfloat",			"getFloatv",			QueryCase::QUERY_FLOAT));
   4883 		stateQueryGroup->addChild(new QueryCase			(m_context,	"getboolean",		"getBooleanv",			QueryCase::QUERY_BOOLEAN));
   4884 		stateQueryGroup->addChild(new QueryCase			(m_context,	"getinteger",		"getIntegerv",			QueryCase::QUERY_INT));
   4885 		stateQueryGroup->addChild(new QueryCase			(m_context,	"getinteger64",		"getInteger64v",		QueryCase::QUERY_INT64));
   4886 	}
   4887 
   4888 	// .triangles
   4889 	// .(wide_)lines
   4890 	// .(wide_)points
   4891 	for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitiveTypes); ++primitiveTypeNdx)
   4892 	{
   4893 		tcu::TestCaseGroup* const primitiveGroup = new tcu::TestCaseGroup(m_testCtx, primitiveTypes[primitiveTypeNdx].name, primitiveTypes[primitiveTypeNdx].description);
   4894 		addChild(primitiveGroup);
   4895 
   4896 		for (int stateSetMethodNdx = 0; stateSetMethodNdx < DE_LENGTH_OF_ARRAY(stateSetMethods); ++stateSetMethodNdx)
   4897 		{
   4898 			tcu::TestCaseGroup* const methodGroup = new tcu::TestCaseGroup(m_testCtx, stateSetMethods[stateSetMethodNdx].name, stateSetMethods[stateSetMethodNdx].description);
   4899 			primitiveGroup->addChild(methodGroup);
   4900 
   4901 			for (int pipelineConfigNdx = 0; pipelineConfigNdx < DE_LENGTH_OF_ARRAY(pipelineConfigs); ++pipelineConfigNdx)
   4902 			{
   4903 				if ((stateSetMethods[stateSetMethodNdx].methodFlags & BBoxRenderCase::FLAG_SET_BBOX_OUTPUT) != 0 &&
   4904 					(pipelineConfigs[pipelineConfigNdx].stageFlags  & BBoxRenderCase::FLAG_TESSELLATION)    == 0)
   4905 				{
   4906 					// invalid config combination
   4907 				}
   4908 				else
   4909 				{
   4910 					tcu::TestCaseGroup* const pipelineGroup = new tcu::TestCaseGroup(m_testCtx, pipelineConfigs[pipelineConfigNdx].name, pipelineConfigs[pipelineConfigNdx].description);
   4911 					methodGroup->addChild(pipelineGroup);
   4912 
   4913 					for (int usageNdx = 0; usageNdx < DE_LENGTH_OF_ARRAY(usageConfigs); ++usageNdx)
   4914 					{
   4915 						const deUint32 flags = primitiveTypes[primitiveTypeNdx].flags         |
   4916 											   stateSetMethods[stateSetMethodNdx].methodFlags |
   4917 											   pipelineConfigs[pipelineConfigNdx].stageFlags  |
   4918 											   usageConfigs[usageNdx].flags;
   4919 
   4920 						if (usageConfigs[usageNdx].invalidFlags && (flags & usageConfigs[usageNdx].invalidFlags) != 0)
   4921 							continue;
   4922 						if (usageConfigs[usageNdx].requiredFlags && (flags & usageConfigs[usageNdx].requiredFlags) == 0)
   4923 							continue;
   4924 
   4925 						switch (primitiveTypes[primitiveTypeNdx].type)
   4926 						{
   4927 							case TYPE_TRIANGLE:
   4928 								pipelineGroup->addChild(new GridRenderCase(m_context, usageConfigs[usageNdx].name, usageConfigs[usageNdx].description, flags));
   4929 								break;
   4930 							case TYPE_LINE:
   4931 								pipelineGroup->addChild(new LineRenderCase(m_context, usageConfigs[usageNdx].name, usageConfigs[usageNdx].description, flags));
   4932 								break;
   4933 							case TYPE_POINT:
   4934 								pipelineGroup->addChild(new PointRenderCase(m_context, usageConfigs[usageNdx].name, usageConfigs[usageNdx].description, flags));
   4935 								break;
   4936 							default:
   4937 								DE_ASSERT(false);
   4938 						}
   4939 					}
   4940 				}
   4941 			}
   4942 		}
   4943 	}
   4944 
   4945 	// .depth
   4946 	{
   4947 		static const struct
   4948 		{
   4949 			const char*					name;
   4950 			const char*					description;
   4951 			DepthDrawCase::DepthType	depthMethod;
   4952 		} depthMethods[] =
   4953 		{
   4954 			{
   4955 				"builtin_depth",
   4956 				"Fragment depth not modified in fragment shader",
   4957 				DepthDrawCase::DEPTH_BUILTIN
   4958 			},
   4959 			{
   4960 				"user_defined_depth",
   4961 				"Fragment depth is defined in the fragment shader",
   4962 				DepthDrawCase::DEPTH_USER_DEFINED
   4963 			},
   4964 		};
   4965 		static const struct
   4966 		{
   4967 			const char*					name;
   4968 			const char*					description;
   4969 			DepthDrawCase::BBoxState	bboxState;
   4970 			DepthDrawCase::BBoxSize		bboxSize;
   4971 		} depthCases[] =
   4972 		{
   4973 			{
   4974 				"global_state_bbox_equal",
   4975 				"Test tight bounding box with global bbox state",
   4976 				DepthDrawCase::STATE_GLOBAL,
   4977 				DepthDrawCase::BBOX_EQUAL,
   4978 			},
   4979 			{
   4980 				"global_state_bbox_larger",
   4981 				"Test padded bounding box with global bbox state",
   4982 				DepthDrawCase::STATE_GLOBAL,
   4983 				DepthDrawCase::BBOX_LARGER,
   4984 			},
   4985 			{
   4986 				"per_primitive_bbox_equal",
   4987 				"Test tight bounding box with tessellation output bbox",
   4988 				DepthDrawCase::STATE_PER_PRIMITIVE,
   4989 				DepthDrawCase::BBOX_EQUAL,
   4990 			},
   4991 			{
   4992 				"per_primitive_bbox_larger",
   4993 				"Test padded bounding box with tessellation output bbox",
   4994 				DepthDrawCase::STATE_PER_PRIMITIVE,
   4995 				DepthDrawCase::BBOX_LARGER,
   4996 			},
   4997 		};
   4998 
   4999 		tcu::TestCaseGroup* const depthGroup = new tcu::TestCaseGroup(m_testCtx, "depth", "Test bounding box depth component");
   5000 		addChild(depthGroup);
   5001 
   5002 		// .builtin_depth
   5003 		// .user_defined_depth
   5004 		for (int depthNdx = 0; depthNdx < DE_LENGTH_OF_ARRAY(depthMethods); ++depthNdx)
   5005 		{
   5006 			tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(m_testCtx, depthMethods[depthNdx].name, depthMethods[depthNdx].description);
   5007 			depthGroup->addChild(group);
   5008 
   5009 			for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(depthCases); ++caseNdx)
   5010 				group->addChild(new DepthDrawCase(m_context, depthCases[caseNdx].name, depthCases[caseNdx].description, depthMethods[depthNdx].depthMethod, depthCases[caseNdx].bboxState, depthCases[caseNdx].bboxSize));
   5011 		}
   5012 	}
   5013 
   5014 	// .blit_fbo
   5015 	{
   5016 		tcu::TestCaseGroup* const blitFboGroup = new tcu::TestCaseGroup(m_testCtx, "blit_fbo", "Test bounding box does not affect blitting");
   5017 		addChild(blitFboGroup);
   5018 
   5019 		blitFboGroup->addChild(new BlitFboCase(m_context, "blit_default_to_fbo", "Blit from default fb to fbo", BlitFboCase::TARGET_DEFAULT, BlitFboCase::TARGET_FBO));
   5020 		blitFboGroup->addChild(new BlitFboCase(m_context, "blit_fbo_to_default", "Blit from fbo to default fb", BlitFboCase::TARGET_FBO,     BlitFboCase::TARGET_DEFAULT));
   5021 		blitFboGroup->addChild(new BlitFboCase(m_context, "blit_fbo_to_fbo",     "Blit from fbo to fbo",        BlitFboCase::TARGET_FBO,     BlitFboCase::TARGET_FBO));
   5022 	}
   5023 
   5024 	// .clear
   5025 	{
   5026 		tcu::TestCaseGroup* const clearGroup = new tcu::TestCaseGroup(m_testCtx, "clear", "Test bounding box does not clears");
   5027 		addChild(clearGroup);
   5028 
   5029 		clearGroup->addChild(new ClearCase(m_context, "full_clear",                                             "Do full clears",                                               0));
   5030 		clearGroup->addChild(new ClearCase(m_context, "full_clear_with_triangles",                              "Do full clears and render some geometry",                      ClearCase::DRAW_TRIANGLE_BIT));
   5031 		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));
   5032 		clearGroup->addChild(new ClearCase(m_context, "scissored_clear",                                        "Do scissored clears",                                          ClearCase::SCISSOR_CLEAR_BIT));
   5033 		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));
   5034 		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));
   5035 		clearGroup->addChild(new ClearCase(m_context, "scissored_full_clear",                                   "Do full clears with enabled scissor",                          ClearCase::FULLSCREEN_SCISSOR_BIT | ClearCase::SCISSOR_CLEAR_BIT));
   5036 		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));
   5037 		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));
   5038 	}
   5039 
   5040 	// .call_order (Khronos bug #13262)
   5041 	{
   5042 		tcu::TestCaseGroup* const callOrderGroup = new tcu::TestCaseGroup(m_testCtx, "call_order", "Test viewport and bounding box calls have no effect");
   5043 		addChild(callOrderGroup);
   5044 
   5045 		callOrderGroup->addChild(new ViewportCallOrderCase(m_context, "viewport_first_bbox_second", "Set up viewport first and bbox after", ViewportCallOrderCase::VIEWPORT_FIRST));
   5046 		callOrderGroup->addChild(new ViewportCallOrderCase(m_context, "bbox_first_viewport_second", "Set up bbox first and viewport after", ViewportCallOrderCase::BBOX_FIRST));
   5047 	}
   5048 }
   5049 
   5050 } // Functional
   5051 } // gles31
   5052 } // deqp
   5053