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