Home | History | Annotate | Download | only in functional
      1 /*-------------------------------------------------------------------------
      2  * drawElements Quality Program OpenGL ES 3.1 Module
      3  * -------------------------------------------------
      4  *
      5  * Copyright 2014 The Android Open Source Project
      6  *
      7  * Licensed under the Apache License, Version 2.0 (the "License");
      8  * you may not use this file except in compliance with the License.
      9  * You may obtain a copy of the License at
     10  *
     11  *      http://www.apache.org/licenses/LICENSE-2.0
     12  *
     13  * Unless required by applicable law or agreed to in writing, software
     14  * distributed under the License is distributed on an "AS IS" BASIS,
     15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     16  * See the License for the specific language governing permissions and
     17  * limitations under the License.
     18  *
     19  *//*!
     20  * \file
     21  * \brief Tessellation and geometry shader interaction tests.
     22  *//*--------------------------------------------------------------------*/
     23 
     24 #include "es31fTessellationGeometryInteractionTests.hpp"
     25 
     26 #include "tcuTestLog.hpp"
     27 #include "tcuRenderTarget.hpp"
     28 #include "tcuSurface.hpp"
     29 #include "tcuImageCompare.hpp"
     30 #include "tcuVectorUtil.hpp"
     31 #include "tcuTextureUtil.hpp"
     32 #include "tcuStringTemplate.hpp"
     33 #include "gluRenderContext.hpp"
     34 #include "gluShaderProgram.hpp"
     35 #include "gluStrUtil.hpp"
     36 #include "gluContextInfo.hpp"
     37 #include "gluObjectWrapper.hpp"
     38 #include "gluPixelTransfer.hpp"
     39 #include "glwFunctions.hpp"
     40 #include "glwEnums.hpp"
     41 #include "deStringUtil.hpp"
     42 #include "deUniquePtr.hpp"
     43 
     44 #include <sstream>
     45 #include <algorithm>
     46 #include <iterator>
     47 
     48 namespace deqp
     49 {
     50 namespace gles31
     51 {
     52 namespace Functional
     53 {
     54 namespace
     55 {
     56 
     57 static std::string specializeShader (const std::string& shaderSource, const glu::ContextType& contextType)
     58 {
     59 	const bool supportsES32 = glu::contextSupports(contextType, glu::ApiType::es(3, 2));
     60 	std::map<std::string, std::string> shaderArgs;
     61 
     62 	shaderArgs["VERSION_DECL"]					= glu::getGLSLVersionDeclaration(glu::getContextTypeGLSLVersion(contextType));
     63 	shaderArgs["EXTENSION_GEOMETRY_SHADER"]		= (supportsES32) ? ("") : ("#extension GL_EXT_geometry_shader : require\n");
     64 	shaderArgs["EXTENSION_TESSELATION_SHADER"]	= (supportsES32) ? ("") : ("#extension GL_EXT_tessellation_shader : require\n");
     65 
     66 	return tcu::StringTemplate(shaderSource).specialize(shaderArgs);
     67 }
     68 
     69 static const char* const s_positionVertexShader =		"${VERSION_DECL}\n"
     70 														"in highp vec4 a_position;\n"
     71 														"void main (void)\n"
     72 														"{\n"
     73 														"	gl_Position = a_position;\n"
     74 														"}\n";
     75 static const char* const s_whiteOutputFragmentShader =	"${VERSION_DECL}\n"
     76 														"layout(location = 0) out mediump vec4 fragColor;\n"
     77 														"void main (void)\n"
     78 														"{\n"
     79 														"	fragColor = vec4(1.0);\n"
     80 														"}\n";
     81 
     82 static bool isBlack (const tcu::RGBA& c)
     83 {
     84 	return c.getRed() == 0 && c.getGreen() == 0 && c.getBlue() == 0;
     85 }
     86 
     87 class IdentityShaderCase : public TestCase
     88 {
     89 public:
     90 					IdentityShaderCase	(Context& context, const char* name, const char* description);
     91 
     92 protected:
     93 	std::string		getVertexSource		(void) const;
     94 	std::string		getFragmentSource	(void) const;
     95 };
     96 
     97 IdentityShaderCase::IdentityShaderCase (Context& context, const char* name, const char* description)
     98 	: TestCase(context, name, description)
     99 {
    100 }
    101 
    102 std::string IdentityShaderCase::getVertexSource (void) const
    103 {
    104 	std::string source =	"${VERSION_DECL}\n"
    105 							"in highp vec4 a_position;\n"
    106 							"out highp vec4 v_vertex_color;\n"
    107 							"void main (void)\n"
    108 							"{\n"
    109 							"	gl_Position = a_position;\n"
    110 							"	v_vertex_color = vec4(a_position.x * 0.5 + 0.5, a_position.y * 0.5 + 0.5, 1.0, 0.4);\n"
    111 							"}\n";
    112 
    113 	return specializeShader(source, m_context.getRenderContext().getType());
    114 }
    115 
    116 std::string IdentityShaderCase::getFragmentSource (void) const
    117 {
    118 	std::string source =	"${VERSION_DECL}\n"
    119 							"in mediump vec4 v_fragment_color;\n"
    120 							"layout(location = 0) out mediump vec4 fragColor;\n"
    121 							"void main (void)\n"
    122 							"{\n"
    123 							"	fragColor = v_fragment_color;\n"
    124 							"}\n";
    125 
    126 return specializeShader(source, m_context.getRenderContext().getType());
    127 }
    128 
    129 class IdentityGeometryShaderCase : public IdentityShaderCase
    130 {
    131 public:
    132 	enum CaseType
    133 	{
    134 		CASE_TRIANGLES = 0,
    135 		CASE_QUADS,
    136 		CASE_ISOLINES,
    137 	};
    138 
    139 					IdentityGeometryShaderCase			(Context& context, const char* name, const char* description, CaseType caseType);
    140 					~IdentityGeometryShaderCase			(void);
    141 
    142 private:
    143 	void			init								(void);
    144 	void			deinit								(void);
    145 	IterateResult	iterate								(void);
    146 
    147 	std::string		getTessellationControlSource		(void) const;
    148 	std::string		getTessellationEvaluationSource		(bool geometryActive) const;
    149 	std::string		getGeometrySource					(void) const;
    150 
    151 	enum
    152 	{
    153 		RENDER_SIZE = 128,
    154 	};
    155 
    156 	const CaseType	m_case;
    157 	deUint32		m_patchBuffer;
    158 };
    159 
    160 IdentityGeometryShaderCase::IdentityGeometryShaderCase (Context& context, const char* name, const char* description, CaseType caseType)
    161 	: IdentityShaderCase	(context, name, description)
    162 	, m_case				(caseType)
    163 	, m_patchBuffer			(0)
    164 {
    165 }
    166 
    167 IdentityGeometryShaderCase::~IdentityGeometryShaderCase (void)
    168 {
    169 	deinit();
    170 }
    171 
    172 void IdentityGeometryShaderCase::init (void)
    173 {
    174 	// Requirements
    175 	const bool supportsES32		= glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2));
    176 
    177 	if (!supportsES32 &&
    178 		(!m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader") ||
    179 		 !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader")))
    180 		throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader and GL_EXT_geometry_shader extensions");
    181 
    182 	if (m_context.getRenderTarget().getWidth() < RENDER_SIZE ||
    183 		m_context.getRenderTarget().getHeight() < RENDER_SIZE)
    184 		throw tcu::NotSupportedError("Test requires " + de::toString<int>(RENDER_SIZE) + "x" + de::toString<int>(RENDER_SIZE) + " or larger render target.");
    185 
    186 	// Log
    187 
    188 	m_testCtx.getLog()
    189 		<< tcu::TestLog::Message
    190 		<< "Testing tessellating shader program output does not change when a passthrough geometry shader is attached.\n"
    191 		<< "Rendering two images, first with and second without a geometry shader. Expecting similar results.\n"
    192 		<< "Using additive blending to detect overlap.\n"
    193 		<< tcu::TestLog::EndMessage;
    194 
    195 	// Resources
    196 
    197 	{
    198 		static const tcu::Vec4 patchBufferData[4] =
    199 		{
    200 			tcu::Vec4( -0.9f, -0.9f, 0.0f, 1.0f ),
    201 			tcu::Vec4( -0.9f,  0.9f, 0.0f, 1.0f ),
    202 			tcu::Vec4(  0.9f, -0.9f, 0.0f, 1.0f ),
    203 			tcu::Vec4(  0.9f,  0.9f, 0.0f, 1.0f ),
    204 		};
    205 
    206 		const glw::Functions& gl = m_context.getRenderContext().getFunctions();
    207 
    208 		gl.genBuffers(1, &m_patchBuffer);
    209 		gl.bindBuffer(GL_ARRAY_BUFFER, m_patchBuffer);
    210 		gl.bufferData(GL_ARRAY_BUFFER, sizeof(patchBufferData), patchBufferData, GL_STATIC_DRAW);
    211 		GLU_EXPECT_NO_ERROR(gl.getError(), "gen buffer");
    212 	}
    213 }
    214 
    215 void IdentityGeometryShaderCase::deinit (void)
    216 {
    217 	if (m_patchBuffer)
    218 	{
    219 		m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_patchBuffer);
    220 		m_patchBuffer = 0;
    221 	}
    222 }
    223 
    224 IdentityGeometryShaderCase::IterateResult IdentityGeometryShaderCase::iterate (void)
    225 {
    226 	const float				innerTessellationLevel	= 14.0f;
    227 	const float				outerTessellationLevel	= 14.0f;
    228 	const glw::Functions&	gl						= m_context.getRenderContext().getFunctions();
    229 	tcu::Surface			resultWithGeometry		(RENDER_SIZE, RENDER_SIZE);
    230 	tcu::Surface			resultWithoutGeometry	(RENDER_SIZE, RENDER_SIZE);
    231 
    232 	const struct
    233 	{
    234 		const char*				name;
    235 		const char*				description;
    236 		bool					containsGeometryShader;
    237 		tcu::PixelBufferAccess	surfaceAccess;
    238 	} renderTargets[] =
    239 	{
    240 		{ "RenderWithGeometryShader",		"Render with geometry shader",		true,	resultWithGeometry.getAccess()		},
    241 		{ "RenderWithoutGeometryShader",	"Render without geometry shader",	false,	resultWithoutGeometry.getAccess()	},
    242 	};
    243 
    244 	gl.viewport(0, 0, RENDER_SIZE, RENDER_SIZE);
    245 	gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
    246 	GLU_EXPECT_NO_ERROR(gl.getError(), "set viewport");
    247 
    248 	gl.enable(GL_BLEND);
    249 	gl.blendFunc(GL_SRC_ALPHA, GL_ONE);
    250 	gl.blendEquation(GL_FUNC_ADD);
    251 	GLU_EXPECT_NO_ERROR(gl.getError(), "set blend");
    252 
    253 	m_testCtx.getLog() << tcu::TestLog::Message << "Tessellation level: inner " << innerTessellationLevel << ", outer " << outerTessellationLevel << tcu::TestLog::EndMessage;
    254 
    255 	// render with and without geometry shader
    256 	for (int renderNdx = 0; renderNdx < DE_LENGTH_OF_ARRAY(renderTargets); ++renderNdx)
    257 	{
    258 		const tcu::ScopedLogSection	section	(m_testCtx.getLog(), renderTargets[renderNdx].name, renderTargets[renderNdx].description);
    259 		glu::ProgramSources			sources;
    260 
    261 		sources	<< glu::VertexSource(getVertexSource())
    262 				<< glu::FragmentSource(getFragmentSource())
    263 				<< glu::TessellationControlSource(getTessellationControlSource())
    264 				<< glu::TessellationEvaluationSource(getTessellationEvaluationSource(renderTargets[renderNdx].containsGeometryShader));
    265 
    266 		if (renderTargets[renderNdx].containsGeometryShader)
    267 			sources << glu::GeometrySource(getGeometrySource());
    268 
    269 		{
    270 			const glu::ShaderProgram	program					(m_context.getRenderContext(), sources);
    271 			const glu::VertexArray		vao						(m_context.getRenderContext());
    272 			const int					posLocation				= gl.getAttribLocation(program.getProgram(), "a_position");
    273 			const int					innerTessellationLoc	= gl.getUniformLocation(program.getProgram(), "u_innerTessellationLevel");
    274 			const int					outerTessellationLoc	= gl.getUniformLocation(program.getProgram(), "u_outerTessellationLevel");
    275 
    276 			m_testCtx.getLog() << program;
    277 
    278 			if (!program.isOk())
    279 				throw tcu::TestError("could not build program");
    280 			if (posLocation == -1)
    281 				throw tcu::TestError("a_position location was -1");
    282 			if (outerTessellationLoc == -1)
    283 				throw tcu::TestError("u_outerTessellationLevel location was -1");
    284 
    285 			gl.bindVertexArray(*vao);
    286 			gl.bindBuffer(GL_ARRAY_BUFFER, m_patchBuffer);
    287 			gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL);
    288 			gl.enableVertexAttribArray(posLocation);
    289 			GLU_EXPECT_NO_ERROR(gl.getError(), "setup attribs");
    290 
    291 			gl.useProgram(program.getProgram());
    292 			gl.uniform1f(outerTessellationLoc, outerTessellationLevel);
    293 
    294 			if (innerTessellationLoc == -1)
    295 				gl.uniform1f(innerTessellationLoc, innerTessellationLevel);
    296 
    297 			GLU_EXPECT_NO_ERROR(gl.getError(), "use program");
    298 
    299 			gl.patchParameteri(GL_PATCH_VERTICES, (m_case == CASE_TRIANGLES) ? (3): (4));
    300 			GLU_EXPECT_NO_ERROR(gl.getError(), "set patch param");
    301 
    302 			gl.clear(GL_COLOR_BUFFER_BIT);
    303 			GLU_EXPECT_NO_ERROR(gl.getError(), "clear");
    304 
    305 			gl.drawArrays(GL_PATCHES, 0, 4);
    306 			GLU_EXPECT_NO_ERROR(gl.getError(), "draw patches");
    307 
    308 			glu::readPixels(m_context.getRenderContext(), 0, 0, renderTargets[renderNdx].surfaceAccess);
    309 		}
    310 	}
    311 
    312 	if (tcu::intThresholdPositionDeviationCompare(m_testCtx.getLog(),
    313 												  "ImageCompare",
    314 												  "Image comparison",
    315 												  resultWithoutGeometry.getAccess(),
    316 												  resultWithGeometry.getAccess(),
    317 												  tcu::UVec4(8, 8, 8, 255),
    318 												  tcu::IVec3(1, 1, 0),
    319 												  true,
    320 												  tcu::COMPARE_LOG_RESULT))
    321 		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
    322 	else
    323 		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
    324 
    325 	return STOP;
    326 }
    327 
    328 std::string IdentityGeometryShaderCase::getTessellationControlSource (void) const
    329 {
    330 	std::ostringstream buf;
    331 
    332 	buf <<	"${VERSION_DECL}\n"
    333 			"${EXTENSION_TESSELATION_SHADER}"
    334 			"layout(vertices = 4) out;\n"
    335 			"\n"
    336 			"uniform highp float u_innerTessellationLevel;\n"
    337 			"uniform highp float u_outerTessellationLevel;\n"
    338 			"in highp vec4 v_vertex_color[];\n"
    339 			"out highp vec4 v_patch_color[];\n"
    340 			"\n"
    341 			"void main (void)\n"
    342 			"{\n"
    343 			"	gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
    344 			"	v_patch_color[gl_InvocationID] = v_vertex_color[gl_InvocationID];\n"
    345 			"\n";
    346 
    347 	if (m_case == CASE_TRIANGLES)
    348 		buf <<	"	gl_TessLevelOuter[0] = u_outerTessellationLevel;\n"
    349 				"	gl_TessLevelOuter[1] = u_outerTessellationLevel;\n"
    350 				"	gl_TessLevelOuter[2] = u_outerTessellationLevel;\n"
    351 				"	gl_TessLevelInner[0] = u_innerTessellationLevel;\n";
    352 	else if (m_case == CASE_QUADS)
    353 		buf <<	"	gl_TessLevelOuter[0] = u_outerTessellationLevel;\n"
    354 				"	gl_TessLevelOuter[1] = u_outerTessellationLevel;\n"
    355 				"	gl_TessLevelOuter[2] = u_outerTessellationLevel;\n"
    356 				"	gl_TessLevelOuter[3] = u_outerTessellationLevel;\n"
    357 				"	gl_TessLevelInner[0] = u_innerTessellationLevel;\n"
    358 				"	gl_TessLevelInner[1] = u_innerTessellationLevel;\n";
    359 	else if (m_case == CASE_ISOLINES)
    360 		buf <<	"	gl_TessLevelOuter[0] = u_outerTessellationLevel;\n"
    361 				"	gl_TessLevelOuter[1] = u_outerTessellationLevel;\n";
    362 	else
    363 		DE_ASSERT(false);
    364 
    365 	buf <<	"}\n";
    366 
    367 	return specializeShader(buf.str(), m_context.getRenderContext().getType());
    368 }
    369 
    370 std::string IdentityGeometryShaderCase::getTessellationEvaluationSource (bool geometryActive) const
    371 {
    372 	const char* const	colorOutputName = ((geometryActive) ? ("v_evaluated_color") : ("v_fragment_color"));
    373 	std::ostringstream	buf;
    374 
    375 	buf <<	"${VERSION_DECL}\n"
    376 			"${EXTENSION_TESSELATION_SHADER}"
    377 			"layout("
    378 				<< ((m_case == CASE_TRIANGLES) ? ("triangles") : (m_case == CASE_QUADS) ? ("quads") : ("isolines"))
    379 				<< ") in;\n"
    380 			"\n"
    381 			"in highp vec4 v_patch_color[];\n"
    382 			"out highp vec4 " << colorOutputName << ";\n"
    383 			"\n"
    384 			"// note: No need to use precise gl_Position since we do not require gapless geometry\n"
    385 			"void main (void)\n"
    386 			"{\n";
    387 
    388 	if (m_case == CASE_TRIANGLES)
    389 		buf <<	"	vec3 weights = vec3(pow(gl_TessCoord.x, 1.3), pow(gl_TessCoord.y, 1.3), pow(gl_TessCoord.z, 1.3));\n"
    390 				"	vec3 cweights = gl_TessCoord;\n"
    391 				"	gl_Position = vec4(weights.x * gl_in[0].gl_Position.xyz + weights.y * gl_in[1].gl_Position.xyz + weights.z * gl_in[2].gl_Position.xyz, 1.0);\n"
    392 				"	" << colorOutputName << " = cweights.x * v_patch_color[0] + cweights.y * v_patch_color[1] + cweights.z * v_patch_color[2];\n";
    393 	else if (m_case == CASE_QUADS || m_case == CASE_ISOLINES)
    394 		buf <<	"	vec2 normalizedCoord = (gl_TessCoord.xy * 2.0 - vec2(1.0));\n"
    395 				"	vec2 normalizedWeights = normalizedCoord * (vec2(1.0) - 0.3 * cos(normalizedCoord.yx * 1.57));\n"
    396 				"	vec2 weights = normalizedWeights * 0.5 + vec2(0.5);\n"
    397 				"	vec2 cweights = gl_TessCoord.xy;\n"
    398 				"	gl_Position = mix(mix(gl_in[0].gl_Position, gl_in[1].gl_Position, weights.y), mix(gl_in[2].gl_Position, gl_in[3].gl_Position, weights.y), weights.x);\n"
    399 				"	" << colorOutputName << " = mix(mix(v_patch_color[0], v_patch_color[1], cweights.y), mix(v_patch_color[2], v_patch_color[3], cweights.y), cweights.x);\n";
    400 	else
    401 		DE_ASSERT(false);
    402 
    403 	buf <<	"}\n";
    404 
    405 	return specializeShader(buf.str(), m_context.getRenderContext().getType());
    406 }
    407 
    408 std::string IdentityGeometryShaderCase::getGeometrySource (void) const
    409 {
    410 	const char* const	geometryInputPrimitive			= (m_case == CASE_ISOLINES) ? ("lines") : ("triangles");
    411 	const char* const	geometryOutputPrimitive			= (m_case == CASE_ISOLINES) ? ("line_strip") : ("triangle_strip");
    412 	const int			numEmitVertices					= (m_case == CASE_ISOLINES) ? (2) : (3);
    413 	std::ostringstream	buf;
    414 
    415 	buf <<	"${VERSION_DECL}\n"
    416 			"${EXTENSION_GEOMETRY_SHADER}"
    417 			"layout(" << geometryInputPrimitive << ") in;\n"
    418 			"layout(" << geometryOutputPrimitive << ", max_vertices=" << numEmitVertices <<") out;\n"
    419 			"\n"
    420 			"in highp vec4 v_evaluated_color[];\n"
    421 			"out highp vec4 v_fragment_color;\n"
    422 			"\n"
    423 			"void main (void)\n"
    424 			"{\n"
    425 			"	for (int ndx = 0; ndx < gl_in.length(); ++ndx)\n"
    426 			"	{\n"
    427 			"		gl_Position = gl_in[ndx].gl_Position;\n"
    428 			"		v_fragment_color = v_evaluated_color[ndx];\n"
    429 			"		EmitVertex();\n"
    430 			"	}\n"
    431 			"}\n";
    432 
    433 	return specializeShader(buf.str(), m_context.getRenderContext().getType());
    434 }
    435 
    436 class IdentityTessellationShaderCase : public IdentityShaderCase
    437 {
    438 public:
    439 	enum CaseType
    440 	{
    441 		CASE_TRIANGLES = 0,
    442 		CASE_ISOLINES,
    443 	};
    444 
    445 					IdentityTessellationShaderCase		(Context& context, const char* name, const char* description, CaseType caseType);
    446 					~IdentityTessellationShaderCase		(void);
    447 
    448 private:
    449 	void			init								(void);
    450 	void			deinit								(void);
    451 	IterateResult	iterate								(void);
    452 
    453 	std::string		getTessellationControlSource		(void) const;
    454 	std::string		getTessellationEvaluationSource		(void) const;
    455 	std::string		getGeometrySource					(bool tessellationActive) const;
    456 
    457 	enum
    458 	{
    459 		RENDER_SIZE = 256,
    460 	};
    461 
    462 	const CaseType	m_case;
    463 	deUint32		m_dataBuffer;
    464 };
    465 
    466 IdentityTessellationShaderCase::IdentityTessellationShaderCase (Context& context, const char* name, const char* description, CaseType caseType)
    467 	: IdentityShaderCase	(context, name, description)
    468 	, m_case				(caseType)
    469 	, m_dataBuffer			(0)
    470 {
    471 }
    472 
    473 IdentityTessellationShaderCase::~IdentityTessellationShaderCase (void)
    474 {
    475 	deinit();
    476 }
    477 
    478 void IdentityTessellationShaderCase::init (void)
    479 {
    480 	// Requirements
    481 	const bool supportsES32		= glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2));
    482 
    483 	if (!supportsES32 &&
    484 		(!m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader") ||
    485 		 !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader")))
    486 		throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader and GL_EXT_geometry_shader extensions");
    487 
    488 	if (m_context.getRenderTarget().getWidth() < RENDER_SIZE ||
    489 		m_context.getRenderTarget().getHeight() < RENDER_SIZE)
    490 		throw tcu::NotSupportedError("Test requires " + de::toString<int>(RENDER_SIZE) + "x" + de::toString<int>(RENDER_SIZE) + " or larger render target.");
    491 
    492 	// Log
    493 
    494 	m_testCtx.getLog()
    495 		<< tcu::TestLog::Message
    496 		<< "Testing geometry shading shader program output does not change when a passthrough tessellation shader is attached.\n"
    497 		<< "Rendering two images, first with and second without a tessellation shader. Expecting similar results.\n"
    498 		<< "Using additive blending to detect overlap.\n"
    499 		<< tcu::TestLog::EndMessage;
    500 
    501 	// Resources
    502 
    503 	{
    504 		static const tcu::Vec4	pointData[]	=
    505 		{
    506 			tcu::Vec4( -0.4f,  0.4f, 0.0f, 1.0f ),
    507 			tcu::Vec4(  0.0f, -0.5f, 0.0f, 1.0f ),
    508 			tcu::Vec4(  0.4f,  0.4f, 0.0f, 1.0f ),
    509 		};
    510 		const glw::Functions&	gl			= m_context.getRenderContext().getFunctions();
    511 
    512 		gl.genBuffers(1, &m_dataBuffer);
    513 		gl.bindBuffer(GL_ARRAY_BUFFER, m_dataBuffer);
    514 		gl.bufferData(GL_ARRAY_BUFFER, sizeof(pointData), pointData, GL_STATIC_DRAW);
    515 		GLU_EXPECT_NO_ERROR(gl.getError(), "gen buffer");
    516 	}
    517 }
    518 
    519 void IdentityTessellationShaderCase::deinit (void)
    520 {
    521 	if (m_dataBuffer)
    522 	{
    523 		m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_dataBuffer);
    524 		m_dataBuffer = 0;
    525 	}
    526 }
    527 
    528 IdentityTessellationShaderCase::IterateResult IdentityTessellationShaderCase::iterate (void)
    529 {
    530 	const glw::Functions&	gl							= m_context.getRenderContext().getFunctions();
    531 	tcu::Surface			resultWithTessellation		(RENDER_SIZE, RENDER_SIZE);
    532 	tcu::Surface			resultWithoutTessellation	(RENDER_SIZE, RENDER_SIZE);
    533 	const int				numPrimitiveVertices		= (m_case == CASE_TRIANGLES) ? (3) : (2);
    534 
    535 	const struct
    536 	{
    537 		const char*				name;
    538 		const char*				description;
    539 		bool					containsTessellationShaders;
    540 		tcu::PixelBufferAccess	surfaceAccess;
    541 	} renderTargets[] =
    542 	{
    543 		{ "RenderWithTessellationShader",		"Render with tessellation shader",		true,	resultWithTessellation.getAccess()		},
    544 		{ "RenderWithoutTessellationShader",	"Render without tessellation shader",	false,	resultWithoutTessellation.getAccess()	},
    545 	};
    546 
    547 	gl.viewport(0, 0, RENDER_SIZE, RENDER_SIZE);
    548 	gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
    549 	GLU_EXPECT_NO_ERROR(gl.getError(), "set viewport");
    550 
    551 	gl.enable(GL_BLEND);
    552 	gl.blendFunc(GL_SRC_ALPHA, GL_ONE);
    553 	gl.blendEquation(GL_FUNC_ADD);
    554 	GLU_EXPECT_NO_ERROR(gl.getError(), "set blend");
    555 
    556 	// render with and without tessellation shader
    557 	for (int renderNdx = 0; renderNdx < DE_LENGTH_OF_ARRAY(renderTargets); ++renderNdx)
    558 	{
    559 		const tcu::ScopedLogSection	section	(m_testCtx.getLog(), renderTargets[renderNdx].name, renderTargets[renderNdx].description);
    560 		glu::ProgramSources			sources;
    561 
    562 		sources	<< glu::VertexSource(getVertexSource())
    563 				<< glu::FragmentSource(getFragmentSource())
    564 				<< glu::GeometrySource(getGeometrySource(renderTargets[renderNdx].containsTessellationShaders));
    565 
    566 		if (renderTargets[renderNdx].containsTessellationShaders)
    567 			sources	<< glu::TessellationControlSource(getTessellationControlSource())
    568 					<< glu::TessellationEvaluationSource(getTessellationEvaluationSource());
    569 
    570 		{
    571 			const glu::ShaderProgram	program					(m_context.getRenderContext(), sources);
    572 			const glu::VertexArray		vao						(m_context.getRenderContext());
    573 			const int					posLocation				= gl.getAttribLocation(program.getProgram(), "a_position");
    574 
    575 			m_testCtx.getLog() << program;
    576 
    577 			if (!program.isOk())
    578 				throw tcu::TestError("could not build program");
    579 			if (posLocation == -1)
    580 				throw tcu::TestError("a_position location was -1");
    581 
    582 			gl.bindVertexArray(*vao);
    583 			gl.bindBuffer(GL_ARRAY_BUFFER, m_dataBuffer);
    584 			gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL);
    585 			gl.enableVertexAttribArray(posLocation);
    586 			GLU_EXPECT_NO_ERROR(gl.getError(), "setup attribs");
    587 
    588 			gl.useProgram(program.getProgram());
    589 			GLU_EXPECT_NO_ERROR(gl.getError(), "use program");
    590 
    591 			gl.clear(GL_COLOR_BUFFER_BIT);
    592 			GLU_EXPECT_NO_ERROR(gl.getError(), "clear");
    593 
    594 			if (renderTargets[renderNdx].containsTessellationShaders)
    595 			{
    596 				gl.patchParameteri(GL_PATCH_VERTICES, numPrimitiveVertices);
    597 				GLU_EXPECT_NO_ERROR(gl.getError(), "set patch param");
    598 
    599 				gl.drawArrays(GL_PATCHES, 0, numPrimitiveVertices);
    600 				GLU_EXPECT_NO_ERROR(gl.getError(), "draw patches");
    601 			}
    602 			else
    603 			{
    604 				gl.drawArrays((m_case == CASE_TRIANGLES) ? (GL_TRIANGLES) : (GL_LINES), 0, numPrimitiveVertices);
    605 				GLU_EXPECT_NO_ERROR(gl.getError(), "draw primitives");
    606 			}
    607 
    608 			glu::readPixels(m_context.getRenderContext(), 0, 0, renderTargets[renderNdx].surfaceAccess);
    609 		}
    610 	}
    611 
    612 	// compare
    613 	{
    614 		bool imageOk;
    615 
    616 		if (m_context.getRenderTarget().getNumSamples() > 1)
    617 			imageOk = tcu::fuzzyCompare(m_testCtx.getLog(),
    618 										"ImageCompare",
    619 										"Image comparison",
    620 										resultWithoutTessellation.getAccess(),
    621 										resultWithTessellation.getAccess(),
    622 										0.03f,
    623 										tcu::COMPARE_LOG_RESULT);
    624 		else
    625 			imageOk = tcu::intThresholdPositionDeviationCompare(m_testCtx.getLog(),
    626 																"ImageCompare",
    627 																"Image comparison",
    628 																resultWithoutTessellation.getAccess(),
    629 																resultWithTessellation.getAccess(),
    630 																tcu::UVec4(8, 8, 8, 255),				//!< threshold
    631 																tcu::IVec3(1, 1, 0),					//!< 3x3 search kernel
    632 																true,									//!< fragments may end up over the viewport, just ignore them
    633 																tcu::COMPARE_LOG_RESULT);
    634 
    635 		if (imageOk)
    636 			m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
    637 		else
    638 			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
    639 	}
    640 
    641 	return STOP;
    642 }
    643 
    644 std::string IdentityTessellationShaderCase::getTessellationControlSource (void) const
    645 {
    646 	std::ostringstream buf;
    647 
    648 	buf <<	"${VERSION_DECL}\n"
    649 			"${EXTENSION_TESSELATION_SHADER}"
    650 			"layout(vertices = " << ((m_case == CASE_TRIANGLES) ? (3) : (2)) << ") out;\n"
    651 			"\n"
    652 			"in highp vec4 v_vertex_color[];\n"
    653 			"out highp vec4 v_control_color[];\n"
    654 			"\n"
    655 			"void main (void)\n"
    656 			"{\n"
    657 			"	gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
    658 			"	v_control_color[gl_InvocationID] = v_vertex_color[gl_InvocationID];\n"
    659 			"\n";
    660 
    661 	if (m_case == CASE_TRIANGLES)
    662 		buf <<	"	gl_TessLevelOuter[0] = 1.0;\n"
    663 				"	gl_TessLevelOuter[1] = 1.0;\n"
    664 				"	gl_TessLevelOuter[2] = 1.0;\n"
    665 				"	gl_TessLevelInner[0] = 1.0;\n";
    666 	else if (m_case == CASE_ISOLINES)
    667 		buf <<	"	gl_TessLevelOuter[0] = 1.0;\n"
    668 				"	gl_TessLevelOuter[1] = 1.0;\n";
    669 	else
    670 		DE_ASSERT(false);
    671 
    672 	buf <<	"}\n";
    673 
    674 	return specializeShader(buf.str(), m_context.getRenderContext().getType());
    675 }
    676 
    677 std::string IdentityTessellationShaderCase::getTessellationEvaluationSource (void) const
    678 {
    679 	std::ostringstream buf;
    680 
    681 	buf <<	"${VERSION_DECL}\n"
    682 			"${EXTENSION_TESSELATION_SHADER}"
    683 			"layout("
    684 				<< ((m_case == CASE_TRIANGLES) ? ("triangles") : ("isolines"))
    685 				<< ") in;\n"
    686 			"\n"
    687 			"in highp vec4 v_control_color[];\n"
    688 			"out highp vec4 v_evaluated_color;\n"
    689 			"\n"
    690 			"// note: No need to use precise gl_Position since we do not require gapless geometry\n"
    691 			"void main (void)\n"
    692 			"{\n";
    693 
    694 	if (m_case == CASE_TRIANGLES)
    695 		buf <<	"	gl_Position = 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"
    696 				"	v_evaluated_color = gl_TessCoord.x * v_control_color[0] + gl_TessCoord.y * v_control_color[1] + gl_TessCoord.z * v_control_color[2];\n";
    697 	else if (m_case == CASE_ISOLINES)
    698 		buf <<	"	gl_Position = mix(gl_in[0].gl_Position, gl_in[1].gl_Position, gl_TessCoord.x);\n"
    699 				"	v_evaluated_color = mix(v_control_color[0], v_control_color[1], gl_TessCoord.x);\n";
    700 	else
    701 		DE_ASSERT(false);
    702 
    703 	buf <<	"}\n";
    704 
    705 	return specializeShader(buf.str(), m_context.getRenderContext().getType());
    706 }
    707 
    708 std::string IdentityTessellationShaderCase::getGeometrySource (bool tessellationActive) const
    709 {
    710 	const char* const	colorSourceName			= (tessellationActive) ? ("v_evaluated_color") : ("v_vertex_color");
    711 	const char* const	geometryInputPrimitive	= (m_case == CASE_ISOLINES) ? ("lines") : ("triangles");
    712 	const char* const	geometryOutputPrimitive	= (m_case == CASE_ISOLINES) ? ("line_strip") : ("triangle_strip");
    713 	const int			numEmitVertices			= (m_case == CASE_ISOLINES) ? (11) : (8);
    714 	std::ostringstream	buf;
    715 
    716 	buf <<	"${VERSION_DECL}\n"
    717 			"${EXTENSION_GEOMETRY_SHADER}"
    718 			"layout(" << geometryInputPrimitive << ") in;\n"
    719 			"layout(" << geometryOutputPrimitive << ", max_vertices=" << numEmitVertices <<") out;\n"
    720 			"\n"
    721 			"in highp vec4 " << colorSourceName << "[];\n"
    722 			"out highp vec4 v_fragment_color;\n"
    723 			"\n"
    724 			"void main (void)\n"
    725 			"{\n";
    726 
    727 	if (m_case == CASE_TRIANGLES)
    728 	{
    729 		buf <<	"	vec4 centerPos = (gl_in[0].gl_Position + gl_in[1].gl_Position + gl_in[2].gl_Position) / 3.0f;\n"
    730 				"\n"
    731 				"	for (int ndx = 0; ndx < 4; ++ndx)\n"
    732 				"	{\n"
    733 				"		gl_Position = centerPos + (centerPos - gl_in[ndx % 3].gl_Position);\n"
    734 				"		v_fragment_color = " << colorSourceName << "[ndx % 3];\n"
    735 				"		EmitVertex();\n"
    736 				"\n"
    737 				"		gl_Position = centerPos + 0.7 * (centerPos - gl_in[ndx % 3].gl_Position);\n"
    738 				"		v_fragment_color = " << colorSourceName << "[ndx % 3];\n"
    739 				"		EmitVertex();\n"
    740 				"	}\n";
    741 
    742 	}
    743 	else if (m_case == CASE_ISOLINES)
    744 	{
    745 		buf <<	"	vec4 mdir = vec4(gl_in[0].gl_Position.y - gl_in[1].gl_Position.y, gl_in[1].gl_Position.x - gl_in[0].gl_Position.x, 0.0, 0.0);\n"
    746 				"	for (int i = 0; i <= 10; ++i)\n"
    747 				"	{\n"
    748 				"		float xweight = cos(float(i) / 10.0 * 6.28) * 0.5 + 0.5;\n"
    749 				"		float mweight = sin(float(i) / 10.0 * 6.28) * 0.1 + 0.1;\n"
    750 				"		gl_Position = mix(gl_in[0].gl_Position, gl_in[1].gl_Position, xweight) + mweight * mdir;\n"
    751 				"		v_fragment_color = mix(" << colorSourceName << "[0], " << colorSourceName << "[1], xweight);\n"
    752 				"		EmitVertex();\n"
    753 				"	}\n";
    754 	}
    755 	else
    756 		DE_ASSERT(false);
    757 
    758 	buf <<	"}\n";
    759 
    760 	return specializeShader(buf.str(), m_context.getRenderContext().getType());
    761 }
    762 
    763 class FeedbackPrimitiveTypeCase : public TestCase
    764 {
    765 public:
    766 	enum TessellationOutputType
    767 	{
    768 		TESSELLATION_OUT_TRIANGLES = 0,
    769 		TESSELLATION_OUT_QUADS,
    770 		TESSELLATION_OUT_ISOLINES,
    771 
    772 		TESSELLATION_OUT_LAST
    773 	};
    774 	enum TessellationPointMode
    775 	{
    776 		TESSELLATION_POINTMODE_OFF = 0,
    777 		TESSELLATION_POINTMODE_ON,
    778 
    779 		TESSELLATION_POINTMODE_LAST
    780 	};
    781 	enum GeometryOutputType
    782 	{
    783 		GEOMETRY_OUTPUT_POINTS = 0,
    784 		GEOMETRY_OUTPUT_LINES,
    785 		GEOMETRY_OUTPUT_TRIANGLES,
    786 
    787 		GEOMETRY_OUTPUT_LAST
    788 	};
    789 
    790 									FeedbackPrimitiveTypeCase				(Context& context,
    791 																			 const char* name,
    792 																			 const char* description,
    793 																			 TessellationOutputType tessellationOutput,
    794 																			 TessellationPointMode tessellationPointMode,
    795 																			 GeometryOutputType geometryOutputType);
    796 									~FeedbackPrimitiveTypeCase				(void);
    797 
    798 private:
    799 	void							init									(void);
    800 	void							deinit									(void);
    801 	IterateResult					iterate									(void);
    802 
    803 	void							renderWithFeedback						(tcu::Surface& dst);
    804 	void							renderWithoutFeedback					(tcu::Surface& dst);
    805 	void							verifyFeedbackResults					(const std::vector<tcu::Vec4>& feedbackResult);
    806 	void							verifyRenderedImage						(const tcu::Surface& image, const std::vector<tcu::Vec4>& vertices);
    807 
    808 	void							genTransformFeedback					(void);
    809 	int								getNumGeneratedElementsPerPrimitive		(void) const;
    810 	int								getNumGeneratedPrimitives				(void) const;
    811 	int								getNumTessellatedPrimitives				(void) const;
    812 	int								getGeometryAmplification				(void) const;
    813 
    814 	std::string						getVertexSource							(void) const;
    815 	std::string						getFragmentSource						(void) const;
    816 	std::string						getTessellationControlSource			(void) const;
    817 	std::string						getTessellationEvaluationSource			(void) const;
    818 	std::string						getGeometrySource						(void) const;
    819 
    820 	static const char*				getTessellationOutputDescription		(TessellationOutputType tessellationOutput,
    821 																			 TessellationPointMode tessellationPointMode);
    822 	static const char*				getGeometryInputDescription				(TessellationOutputType tessellationOutput,
    823 																			 TessellationPointMode tessellationPointMode);
    824 	static const char*				getGeometryOutputDescription			(GeometryOutputType geometryOutput);
    825 	glw::GLenum						getOutputPrimitiveGLType				(void) const;
    826 
    827 	enum
    828 	{
    829 		RENDER_SIZE = 128,
    830 	};
    831 
    832 	const TessellationOutputType	m_tessellationOutput;
    833 	const TessellationPointMode		m_tessellationPointMode;
    834 	const GeometryOutputType		m_geometryOutputType;
    835 
    836 	glu::ShaderProgram*				m_feedbackProgram;
    837 	glu::ShaderProgram*				m_nonFeedbackProgram;
    838 	deUint32						m_patchBuffer;
    839 	deUint32						m_feedbackID;
    840 	deUint32						m_feedbackBuffer;
    841 };
    842 
    843 FeedbackPrimitiveTypeCase::FeedbackPrimitiveTypeCase (Context& context,
    844 									  const char* name,
    845 									  const char* description,
    846 									  TessellationOutputType tessellationOutput,
    847 									  TessellationPointMode tessellationPointMode,
    848 									  GeometryOutputType geometryOutputType)
    849 	: TestCase					(context, name, description)
    850 	, m_tessellationOutput		(tessellationOutput)
    851 	, m_tessellationPointMode	(tessellationPointMode)
    852 	, m_geometryOutputType		(geometryOutputType)
    853 	, m_feedbackProgram			(DE_NULL)
    854 	, m_nonFeedbackProgram		(DE_NULL)
    855 	, m_patchBuffer				(0)
    856 	, m_feedbackID				(0)
    857 	, m_feedbackBuffer			(0)
    858 {
    859 	DE_ASSERT(tessellationOutput < TESSELLATION_OUT_LAST);
    860 	DE_ASSERT(tessellationPointMode < TESSELLATION_POINTMODE_LAST);
    861 	DE_ASSERT(geometryOutputType < GEOMETRY_OUTPUT_LAST);
    862 }
    863 
    864 FeedbackPrimitiveTypeCase::~FeedbackPrimitiveTypeCase (void)
    865 {
    866 	deinit();
    867 }
    868 
    869 void FeedbackPrimitiveTypeCase::init (void)
    870 {
    871 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
    872 
    873 	// Requirements
    874 	const bool supportsES32		= glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2));
    875 
    876 	if (!supportsES32 &&
    877 		(!m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader") ||
    878 		 !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader")))
    879 		throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader and GL_EXT_geometry_shader extensions");
    880 
    881 	if (m_context.getRenderTarget().getWidth() < RENDER_SIZE ||
    882 		m_context.getRenderTarget().getHeight() < RENDER_SIZE)
    883 		throw tcu::NotSupportedError("Test requires " + de::toString<int>(RENDER_SIZE) + "x" + de::toString<int>(RENDER_SIZE) + " or larger render target.");
    884 
    885 	// Log
    886 
    887 	m_testCtx.getLog()
    888 		<< tcu::TestLog::Message
    889 		<< "Testing "
    890 			<< getTessellationOutputDescription(m_tessellationOutput, m_tessellationPointMode)
    891 			<< "->"
    892 			<< getGeometryInputDescription(m_tessellationOutput, m_tessellationPointMode)
    893 			<< " primitive conversion with and without transform feedback.\n"
    894 		<< "Sending a patch of 4 vertices (2x2 uniform grid) to tessellation control shader.\n"
    895 		<< "Control shader emits a patch of 9 vertices (3x3 uniform grid).\n"
    896 		<< "Setting outer tessellation level = 3, inner = 3.\n"
    897 		<< "Primitive generator emits " << getTessellationOutputDescription(m_tessellationOutput, m_tessellationPointMode) << "\n"
    898 		<< "Geometry shader transforms emitted primitives to " << getGeometryOutputDescription(m_geometryOutputType) << "\n"
    899 		<< "Reading back vertex positions of generated primitives using transform feedback.\n"
    900 		<< "Verifying rendered image and feedback vertices are consistent.\n"
    901 		<< "Rendering scene again with identical shader program, but without setting feedback varying. Expecting similar output image."
    902 		<< tcu::TestLog::EndMessage;
    903 
    904 	// Resources
    905 
    906 	{
    907 		static const tcu::Vec4 patchBufferData[4] =
    908 		{
    909 			tcu::Vec4( -0.9f, -0.9f, 0.0f, 1.0f ),
    910 			tcu::Vec4( -0.9f,  0.9f, 0.0f, 1.0f ),
    911 			tcu::Vec4(  0.9f, -0.9f, 0.0f, 1.0f ),
    912 			tcu::Vec4(  0.9f,  0.9f, 0.0f, 1.0f ),
    913 		};
    914 
    915 		gl.genBuffers(1, &m_patchBuffer);
    916 		gl.bindBuffer(GL_ARRAY_BUFFER, m_patchBuffer);
    917 		gl.bufferData(GL_ARRAY_BUFFER, sizeof(patchBufferData), patchBufferData, GL_STATIC_DRAW);
    918 		GLU_EXPECT_NO_ERROR(gl.getError(), "gen buffer");
    919 	}
    920 
    921 	m_feedbackProgram = new glu::ShaderProgram(m_context.getRenderContext(),
    922 											   glu::ProgramSources()
    923 												<< glu::VertexSource(getVertexSource())
    924 												<< glu::FragmentSource(getFragmentSource())
    925 												<< glu::TessellationControlSource(getTessellationControlSource())
    926 												<< glu::TessellationEvaluationSource(getTessellationEvaluationSource())
    927 												<< glu::GeometrySource(getGeometrySource())
    928 												<< glu::TransformFeedbackVarying("tf_someVertexPosition")
    929 												<< glu::TransformFeedbackMode(GL_INTERLEAVED_ATTRIBS));
    930 	m_testCtx.getLog() << *m_feedbackProgram;
    931 	if (!m_feedbackProgram->isOk())
    932 		throw tcu::TestError("failed to build program");
    933 
    934 	m_nonFeedbackProgram = new glu::ShaderProgram(m_context.getRenderContext(),
    935 												  glu::ProgramSources()
    936 													<< glu::VertexSource(getVertexSource())
    937 													<< glu::FragmentSource(getFragmentSource())
    938 													<< glu::TessellationControlSource(getTessellationControlSource())
    939 													<< glu::TessellationEvaluationSource(getTessellationEvaluationSource())
    940 													<< glu::GeometrySource(getGeometrySource()));
    941 	if (!m_nonFeedbackProgram->isOk())
    942 	{
    943 		m_testCtx.getLog() << *m_nonFeedbackProgram;
    944 		throw tcu::TestError("failed to build program");
    945 	}
    946 
    947 	genTransformFeedback();
    948 }
    949 
    950 void FeedbackPrimitiveTypeCase::deinit (void)
    951 {
    952 	if (m_patchBuffer)
    953 	{
    954 		m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_patchBuffer);
    955 		m_patchBuffer = 0;
    956 	}
    957 
    958 	if (m_feedbackBuffer)
    959 	{
    960 		m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_feedbackBuffer);
    961 		m_feedbackBuffer = 0;
    962 	}
    963 
    964 	if (m_feedbackID)
    965 	{
    966 		m_context.getRenderContext().getFunctions().deleteTransformFeedbacks(1, &m_feedbackID);
    967 		m_feedbackID = 0;
    968 	}
    969 
    970 	if (m_feedbackProgram)
    971 	{
    972 		delete m_feedbackProgram;
    973 		m_feedbackProgram = DE_NULL;
    974 	}
    975 
    976 	if (m_nonFeedbackProgram)
    977 	{
    978 		delete m_nonFeedbackProgram;
    979 		m_nonFeedbackProgram = DE_NULL;
    980 	}
    981 }
    982 
    983 FeedbackPrimitiveTypeCase::IterateResult FeedbackPrimitiveTypeCase::iterate (void)
    984 {
    985 	tcu::Surface feedbackResult		(RENDER_SIZE, RENDER_SIZE);
    986 	tcu::Surface nonFeedbackResult	(RENDER_SIZE, RENDER_SIZE);
    987 
    988 	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
    989 
    990 	// render with and without XFB
    991 	renderWithFeedback(feedbackResult);
    992 	renderWithoutFeedback(nonFeedbackResult);
    993 
    994 	// compare
    995 	{
    996 		bool imageOk;
    997 
    998 		m_testCtx.getLog() << tcu::TestLog::Message << "Comparing the image rendered with no transform feedback against the image rendered with enabled transform feedback." << tcu::TestLog::EndMessage;
    999 
   1000 		if (m_context.getRenderTarget().getNumSamples() > 1)
   1001 			imageOk = tcu::fuzzyCompare(m_testCtx.getLog(),
   1002 										"ImageCompare",
   1003 										"Image comparison",
   1004 										feedbackResult.getAccess(),
   1005 										nonFeedbackResult.getAccess(),
   1006 										0.03f,
   1007 										tcu::COMPARE_LOG_RESULT);
   1008 		else
   1009 			imageOk = tcu::intThresholdPositionDeviationCompare(m_testCtx.getLog(),
   1010 																"ImageCompare",
   1011 																"Image comparison",
   1012 																feedbackResult.getAccess(),
   1013 																nonFeedbackResult.getAccess(),
   1014 																tcu::UVec4(8, 8, 8, 255),						//!< threshold
   1015 																tcu::IVec3(1, 1, 0),							//!< 3x3 search kernel
   1016 																true,											//!< fragments may end up over the viewport, just ignore them
   1017 																tcu::COMPARE_LOG_RESULT);
   1018 
   1019 		if (!imageOk)
   1020 			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
   1021 	}
   1022 
   1023 	return STOP;
   1024 }
   1025 
   1026 void FeedbackPrimitiveTypeCase::renderWithFeedback(tcu::Surface& dst)
   1027 {
   1028 	const glw::Functions&			gl							= m_context.getRenderContext().getFunctions();
   1029 	const glu::VertexArray			vao							(m_context.getRenderContext());
   1030 	const glu::Query				primitivesGeneratedQuery	(m_context.getRenderContext());
   1031 	const int						posLocation					= gl.getAttribLocation(m_feedbackProgram->getProgram(), "a_position");
   1032 	const glw::GLenum				feedbackPrimitiveMode		= getOutputPrimitiveGLType();
   1033 
   1034 	if (posLocation == -1)
   1035 		throw tcu::TestError("a_position was -1");
   1036 
   1037 	m_testCtx.getLog() << tcu::TestLog::Message << "Rendering with transform feedback" << tcu::TestLog::EndMessage;
   1038 
   1039 	gl.viewport(0, 0, dst.getWidth(), dst.getHeight());
   1040 	gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
   1041 	gl.clear(GL_COLOR_BUFFER_BIT);
   1042 	GLU_EXPECT_NO_ERROR(gl.getError(), "clear");
   1043 
   1044 	gl.bindVertexArray(*vao);
   1045 	gl.bindBuffer(GL_ARRAY_BUFFER, m_patchBuffer);
   1046 	gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL);
   1047 	gl.enableVertexAttribArray(posLocation);
   1048 	GLU_EXPECT_NO_ERROR(gl.getError(), "setup attribs");
   1049 
   1050 	gl.useProgram(m_feedbackProgram->getProgram());
   1051 	GLU_EXPECT_NO_ERROR(gl.getError(), "use program");
   1052 
   1053 	gl.patchParameteri(GL_PATCH_VERTICES, 4);
   1054 	GLU_EXPECT_NO_ERROR(gl.getError(), "set patch param");
   1055 
   1056 	gl.beginQuery(GL_PRIMITIVES_GENERATED, *primitivesGeneratedQuery);
   1057 	GLU_EXPECT_NO_ERROR(gl.getError(), "begin GL_PRIMITIVES_GENERATED query");
   1058 
   1059 	m_testCtx.getLog() << tcu::TestLog::Message << "Begin transform feedback with mode " << glu::getPrimitiveTypeStr(feedbackPrimitiveMode) << tcu::TestLog::EndMessage;
   1060 
   1061 	gl.beginTransformFeedback(feedbackPrimitiveMode);
   1062 	GLU_EXPECT_NO_ERROR(gl.getError(), "begin xfb");
   1063 
   1064 	m_testCtx.getLog() << tcu::TestLog::Message << "Calling drawArrays with mode GL_PATCHES" << tcu::TestLog::EndMessage;
   1065 
   1066 	gl.drawArrays(GL_PATCHES, 0, 4);
   1067 	GLU_EXPECT_NO_ERROR(gl.getError(), "draw patches");
   1068 
   1069 	gl.endTransformFeedback();
   1070 	GLU_EXPECT_NO_ERROR(gl.getError(), "end xfb");
   1071 
   1072 	gl.endQuery(GL_PRIMITIVES_GENERATED);
   1073 	GLU_EXPECT_NO_ERROR(gl.getError(), "end GL_PRIMITIVES_GENERATED query");
   1074 
   1075 	glu::readPixels(m_context.getRenderContext(), 0, 0, dst.getAccess());
   1076 	GLU_EXPECT_NO_ERROR(gl.getError(), "readPixels");
   1077 
   1078 	// verify GL_PRIMITIVES_GENERATED
   1079 	{
   1080 		glw::GLuint primitivesGeneratedResult = 0;
   1081 		gl.getQueryObjectuiv(*primitivesGeneratedQuery, GL_QUERY_RESULT, &primitivesGeneratedResult);
   1082 		GLU_EXPECT_NO_ERROR(gl.getError(), "get GL_PRIMITIVES_GENERATED value");
   1083 
   1084 		m_testCtx.getLog() << tcu::TestLog::Message << "Verifying GL_PRIMITIVES_GENERATED, expecting " << getNumGeneratedPrimitives() << tcu::TestLog::EndMessage;
   1085 
   1086 		if ((int)primitivesGeneratedResult != getNumGeneratedPrimitives())
   1087 		{
   1088 			m_testCtx.getLog() << tcu::TestLog::Message << "Error, GL_PRIMITIVES_GENERATED was " << primitivesGeneratedResult << tcu::TestLog::EndMessage;
   1089 			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got unexpected GL_PRIMITIVES_GENERATED");
   1090 		}
   1091 		else
   1092 			m_testCtx.getLog() << tcu::TestLog::Message << "GL_PRIMITIVES_GENERATED valid." << tcu::TestLog::EndMessage;
   1093 	}
   1094 
   1095 	// feedback
   1096 	{
   1097 		std::vector<tcu::Vec4>	feedbackResults		(getNumGeneratedElementsPerPrimitive() * getNumGeneratedPrimitives());
   1098 		const void*				mappedPtr			= gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, (glw::GLsizeiptr)(feedbackResults.size() * sizeof(tcu::Vec4)), GL_MAP_READ_BIT);
   1099 		glw::GLboolean			unmapResult;
   1100 
   1101 		GLU_EXPECT_NO_ERROR(gl.getError(), "mapBufferRange");
   1102 
   1103 		m_testCtx.getLog() << tcu::TestLog::Message << "Reading transform feedback buffer." << tcu::TestLog::EndMessage;
   1104 		if (!mappedPtr)
   1105 			throw tcu::TestError("mapBufferRange returned null");
   1106 
   1107 		deMemcpy(feedbackResults[0].getPtr(), mappedPtr, (int)(feedbackResults.size() * sizeof(tcu::Vec4)));
   1108 
   1109 		unmapResult = gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
   1110 		GLU_EXPECT_NO_ERROR(gl.getError(), "unmapBuffer");
   1111 
   1112 		if (unmapResult != GL_TRUE)
   1113 			throw tcu::TestError("unmapBuffer failed, did not return true");
   1114 
   1115 		// verify transform results
   1116 		verifyFeedbackResults(feedbackResults);
   1117 
   1118 		// verify feedback results are consistent with rendered image
   1119 		verifyRenderedImage(dst, feedbackResults);
   1120 	}
   1121 }
   1122 
   1123 void FeedbackPrimitiveTypeCase::renderWithoutFeedback (tcu::Surface& dst)
   1124 {
   1125 	const glw::Functions&			gl							= m_context.getRenderContext().getFunctions();
   1126 	const glu::VertexArray			vao							(m_context.getRenderContext());
   1127 	const int						posLocation					= gl.getAttribLocation(m_nonFeedbackProgram->getProgram(), "a_position");
   1128 
   1129 	if (posLocation == -1)
   1130 		throw tcu::TestError("a_position was -1");
   1131 
   1132 	m_testCtx.getLog() << tcu::TestLog::Message << "Rendering without transform feedback" << tcu::TestLog::EndMessage;
   1133 
   1134 	gl.viewport(0, 0, dst.getWidth(), dst.getHeight());
   1135 	gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
   1136 	gl.clear(GL_COLOR_BUFFER_BIT);
   1137 	GLU_EXPECT_NO_ERROR(gl.getError(), "clear");
   1138 
   1139 	gl.bindVertexArray(*vao);
   1140 	gl.bindBuffer(GL_ARRAY_BUFFER, m_patchBuffer);
   1141 	gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL);
   1142 	gl.enableVertexAttribArray(posLocation);
   1143 	GLU_EXPECT_NO_ERROR(gl.getError(), "setup attribs");
   1144 
   1145 	gl.useProgram(m_nonFeedbackProgram->getProgram());
   1146 	GLU_EXPECT_NO_ERROR(gl.getError(), "use program");
   1147 
   1148 	gl.patchParameteri(GL_PATCH_VERTICES, 4);
   1149 	GLU_EXPECT_NO_ERROR(gl.getError(), "set patch param");
   1150 
   1151 	m_testCtx.getLog() << tcu::TestLog::Message << "Calling drawArrays with mode GL_PATCHES" << tcu::TestLog::EndMessage;
   1152 
   1153 	gl.drawArrays(GL_PATCHES, 0, 4);
   1154 	GLU_EXPECT_NO_ERROR(gl.getError(), "draw patches");
   1155 
   1156 	glu::readPixels(m_context.getRenderContext(), 0, 0, dst.getAccess());
   1157 	GLU_EXPECT_NO_ERROR(gl.getError(), "readPixels");
   1158 }
   1159 
   1160 void FeedbackPrimitiveTypeCase::verifyFeedbackResults (const std::vector<tcu::Vec4>& feedbackResult)
   1161 {
   1162 	const int	geometryAmplification	= getGeometryAmplification();
   1163 	const int	elementsPerPrimitive	= getNumGeneratedElementsPerPrimitive();
   1164 	const int	errorFloodThreshold		= 8;
   1165 	int			readNdx					= 0;
   1166 	int			numErrors				= 0;
   1167 
   1168 	m_testCtx.getLog() << tcu::TestLog::Message << "Verifying feedback results." << tcu::TestLog::EndMessage;
   1169 
   1170 	for (int tessellatedPrimitiveNdx = 0; tessellatedPrimitiveNdx < getNumTessellatedPrimitives(); ++tessellatedPrimitiveNdx)
   1171 	{
   1172 		const tcu::Vec4	primitiveVertex = feedbackResult[readNdx];
   1173 
   1174 		// check the generated vertices are in the proper range (range: -0.4 <-> 0.4)
   1175 		{
   1176 			const float	equalThreshold	=	1.0e-6f;
   1177 			const bool	centroidOk		=	(primitiveVertex.x() >= -0.4f - equalThreshold) &&
   1178 											(primitiveVertex.x() <=  0.4f + equalThreshold) &&
   1179 											(primitiveVertex.y() >= -0.4f - equalThreshold) &&
   1180 											(primitiveVertex.y() <=  0.4f + equalThreshold) &&
   1181 											(de::abs(primitiveVertex.z()) < equalThreshold) &&
   1182 											(de::abs(primitiveVertex.w() - 1.0f) < equalThreshold);
   1183 
   1184 			if (!centroidOk && numErrors++ < errorFloodThreshold)
   1185 			{
   1186 				m_testCtx.getLog()
   1187 					<< tcu::TestLog::Message
   1188 					<< "Element at index " << (readNdx) << " (tessellation invocation " << tessellatedPrimitiveNdx << ")\n"
   1189 					<< "\texpected vertex in range: ( [-0.4, 0.4], [-0.4, 0.4], 0.0, 1.0 )\n"
   1190 					<< "\tgot: " << primitiveVertex
   1191 					<< tcu::TestLog::EndMessage;
   1192 
   1193 				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "invalid feedback output");
   1194 
   1195 				++readNdx;
   1196 				continue;
   1197 			}
   1198 		}
   1199 
   1200 		// check all other primitives generated from this tessellated primitive have the same feedback value
   1201 		for (int generatedPrimitiveNdx = 0; generatedPrimitiveNdx < geometryAmplification; ++generatedPrimitiveNdx)
   1202 		for (int primitiveVertexNdx = 0; primitiveVertexNdx < elementsPerPrimitive; ++primitiveVertexNdx)
   1203 		{
   1204 			const tcu::Vec4 generatedElementVertex	= feedbackResult[readNdx];
   1205 			const tcu::Vec4 equalThreshold			(1.0e-6f);
   1206 
   1207 			if (tcu::boolAny(tcu::greaterThan(tcu::abs(primitiveVertex - generatedElementVertex), equalThreshold)))
   1208 			{
   1209 				if (numErrors++ < errorFloodThreshold)
   1210 				{
   1211 					m_testCtx.getLog()
   1212 						<< tcu::TestLog::Message
   1213 						<< "Element at index " << (readNdx) << " (tessellation invocation " << tessellatedPrimitiveNdx << ", geometry primitive " << generatedPrimitiveNdx << ", emitted vertex " << primitiveVertexNdx << "):\n"
   1214 						<< "\tfeedback result was not contant over whole primitive.\n"
   1215 						<< "\tfirst emitted value: " << primitiveVertex << "\n"
   1216 						<< "\tcurrent emitted value:" << generatedElementVertex << "\n"
   1217 						<< tcu::TestLog::EndMessage;
   1218 				}
   1219 
   1220 				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got multiple different feedback values for a single primitive");
   1221 			}
   1222 
   1223 			readNdx++;
   1224 		}
   1225 	}
   1226 
   1227 	if (numErrors > errorFloodThreshold)
   1228 		m_testCtx.getLog() << tcu::TestLog::Message << "Omitted " << (numErrors - errorFloodThreshold) << " error(s)." << tcu::TestLog::EndMessage;
   1229 }
   1230 
   1231 static bool feedbackResultCompare (const tcu::Vec4& a, const tcu::Vec4& b)
   1232 {
   1233 	if (a.x() < b.x())
   1234 		return true;
   1235 	if (a.x() > b.x())
   1236 		return false;
   1237 
   1238 	return a.y() < b.y();
   1239 }
   1240 
   1241 void FeedbackPrimitiveTypeCase::verifyRenderedImage (const tcu::Surface& image, const std::vector<tcu::Vec4>& tfVertices)
   1242 {
   1243 	std::vector<tcu::Vec4> vertices;
   1244 
   1245 	m_testCtx.getLog() << tcu::TestLog::Message << "Comparing result image against feedback results." << tcu::TestLog::EndMessage;
   1246 
   1247 	// Check only unique vertices
   1248 	std::unique_copy(tfVertices.begin(), tfVertices.end(), std::back_insert_iterator<std::vector<tcu::Vec4> >(vertices));
   1249 	std::sort(vertices.begin(), vertices.end(), feedbackResultCompare);
   1250 	vertices.erase(std::unique(vertices.begin(), vertices.end()), vertices.end());
   1251 
   1252 	// Verifying vertices recorded with feedback actually ended up on the result image
   1253 	for (int ndx = 0; ndx < (int)vertices.size(); ++ndx)
   1254 	{
   1255 		// Rasterization (of lines) may deviate by one pixel. In addition to that, allow minimal errors in rasterized position vs. feedback result.
   1256 		// This minimal error could result in a difference in rounding => allow one additional pixel in deviation
   1257 
   1258 		const int			rasterDeviation	= 2;
   1259 		const tcu::IVec2	rasterPos		((int)deFloatRound((vertices[ndx].x() * 0.5f + 0.5f) * (float)image.getWidth()), (int)deFloatRound((vertices[ndx].y() * 0.5f + 0.5f) * (float)image.getHeight()));
   1260 
   1261 		// Find produced rasterization results
   1262 		bool				found			= false;
   1263 
   1264 		for (int dy = -rasterDeviation; dy <= rasterDeviation && !found; ++dy)
   1265 		for (int dx = -rasterDeviation; dx <= rasterDeviation && !found; ++dx)
   1266 		{
   1267 			// Raster result could end up outside the viewport
   1268 			if (rasterPos.x() + dx < 0 || rasterPos.x() + dx >= image.getWidth() ||
   1269 				rasterPos.y() + dy < 0 || rasterPos.y() + dy >= image.getHeight())
   1270 				found = true;
   1271 			else
   1272 			{
   1273 				const tcu::RGBA result = image.getPixel(rasterPos.x() + dx, rasterPos.y() + dy);
   1274 
   1275 				if(!isBlack(result))
   1276 					found = true;
   1277 			}
   1278 		}
   1279 
   1280 		if (!found)
   1281 		{
   1282 			m_testCtx.getLog()
   1283 				<< tcu::TestLog::Message
   1284 				<< "Vertex " << vertices[ndx] << "\n"
   1285 				<< "\tCould not find rasterization output for vertex.\n"
   1286 				<< "\tExpected non-black pixels near " << rasterPos
   1287 				<< tcu::TestLog::EndMessage;
   1288 
   1289 			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "invalid result image");
   1290 		}
   1291 	}
   1292 }
   1293 
   1294 void FeedbackPrimitiveTypeCase::genTransformFeedback (void)
   1295 {
   1296 	const glw::Functions&			gl						= m_context.getRenderContext().getFunctions();
   1297 	const int						elementsPerPrimitive	= getNumGeneratedElementsPerPrimitive();
   1298 	const int						feedbackPrimitives		= getNumGeneratedPrimitives();
   1299 	const int						feedbackElements		= elementsPerPrimitive * feedbackPrimitives;
   1300 	const std::vector<tcu::Vec4>	initialBuffer			(feedbackElements, tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.0f));
   1301 
   1302 	gl.genTransformFeedbacks(1, &m_feedbackID);
   1303 	gl.bindTransformFeedback(GL_TRANSFORM_FEEDBACK, m_feedbackID);
   1304 	GLU_EXPECT_NO_ERROR(gl.getError(), "gen transform feedback");
   1305 
   1306 	gl.genBuffers(1, &m_feedbackBuffer);
   1307 	gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_feedbackBuffer);
   1308 	gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, sizeof(tcu::Vec4) * initialBuffer.size(), initialBuffer[0].getPtr(), GL_STATIC_COPY);
   1309 	GLU_EXPECT_NO_ERROR(gl.getError(), "gen feedback buffer");
   1310 
   1311 	gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_feedbackBuffer);
   1312 	GLU_EXPECT_NO_ERROR(gl.getError(), "bind feedback buffer");
   1313 }
   1314 
   1315 static int getTriangleNumOutputPrimitives (int tessellationLevel)
   1316 {
   1317 	if (tessellationLevel == 1)
   1318 		return 1;
   1319 	else if (tessellationLevel == 2)
   1320 		return 6;
   1321 	else
   1322 		return 3 * (2 + 2 * (tessellationLevel - 2)) + getTriangleNumOutputPrimitives(tessellationLevel - 2);
   1323 }
   1324 
   1325 static int getTriangleNumOutputPrimitivesPoints (int tessellationLevel)
   1326 {
   1327 	if (tessellationLevel == 0)
   1328 		return 1;
   1329 	else if (tessellationLevel == 1)
   1330 		return 3;
   1331 	else
   1332 		return 3 + 3 * (tessellationLevel - 1) + getTriangleNumOutputPrimitivesPoints(tessellationLevel - 2);
   1333 }
   1334 
   1335 int FeedbackPrimitiveTypeCase::getNumGeneratedElementsPerPrimitive (void) const
   1336 {
   1337 	if (m_geometryOutputType == GEOMETRY_OUTPUT_TRIANGLES)
   1338 		return 3;
   1339 	else if (m_geometryOutputType == GEOMETRY_OUTPUT_LINES)
   1340 		return 2;
   1341 	else if (m_geometryOutputType == GEOMETRY_OUTPUT_POINTS)
   1342 		return 1;
   1343 	else
   1344 	{
   1345 		DE_ASSERT(false);
   1346 		return -1;
   1347 	}
   1348 }
   1349 
   1350 int FeedbackPrimitiveTypeCase::getNumGeneratedPrimitives (void) const
   1351 {
   1352 	return getNumTessellatedPrimitives() * getGeometryAmplification();
   1353 }
   1354 
   1355 int FeedbackPrimitiveTypeCase::getNumTessellatedPrimitives (void) const
   1356 {
   1357 	const int tessellationLevel = 3;
   1358 
   1359 	if (m_tessellationPointMode == TESSELLATION_POINTMODE_OFF)
   1360 	{
   1361 		if (m_tessellationOutput == TESSELLATION_OUT_TRIANGLES)
   1362 			return getTriangleNumOutputPrimitives(tessellationLevel);
   1363 		else if (m_tessellationOutput == TESSELLATION_OUT_QUADS)
   1364 			return tessellationLevel * tessellationLevel * 2; // tessellated as triangles
   1365 		else if (m_tessellationOutput == TESSELLATION_OUT_ISOLINES)
   1366 			return tessellationLevel * tessellationLevel;
   1367 	}
   1368 	else if (m_tessellationPointMode == TESSELLATION_POINTMODE_ON)
   1369 	{
   1370 		if (m_tessellationOutput == TESSELLATION_OUT_TRIANGLES)
   1371 			return getTriangleNumOutputPrimitivesPoints(tessellationLevel);
   1372 		else if (m_tessellationOutput == TESSELLATION_OUT_QUADS)
   1373 			return (tessellationLevel + 1) * (tessellationLevel + 1);
   1374 		else if (m_tessellationOutput == TESSELLATION_OUT_ISOLINES)
   1375 			return tessellationLevel * (tessellationLevel + 1);
   1376 	}
   1377 
   1378 	DE_ASSERT(false);
   1379 	return -1;
   1380 }
   1381 
   1382 int FeedbackPrimitiveTypeCase::getGeometryAmplification (void) const
   1383 {
   1384 	const int outputAmplification	= (m_geometryOutputType == GEOMETRY_OUTPUT_LINES) ? (2) : (1);
   1385 	const int numInputVertices		= (m_tessellationPointMode) ? (1) : (m_tessellationOutput == TESSELLATION_OUT_ISOLINES) ? (2) : (3);
   1386 
   1387 	return outputAmplification * numInputVertices;
   1388 }
   1389 
   1390 glw::GLenum FeedbackPrimitiveTypeCase::getOutputPrimitiveGLType (void) const
   1391 {
   1392 	if (m_geometryOutputType == GEOMETRY_OUTPUT_TRIANGLES)
   1393 		return GL_TRIANGLES;
   1394 	else if (m_geometryOutputType == GEOMETRY_OUTPUT_LINES)
   1395 		return GL_LINES;
   1396 	else if (m_geometryOutputType == GEOMETRY_OUTPUT_POINTS)
   1397 		return GL_POINTS;
   1398 	else
   1399 	{
   1400 		DE_ASSERT(false);
   1401 		return -1;
   1402 	}
   1403 }
   1404 
   1405 std::string FeedbackPrimitiveTypeCase::getVertexSource (void) const
   1406 {
   1407 	return specializeShader(s_positionVertexShader, m_context.getRenderContext().getType());
   1408 }
   1409 
   1410 std::string FeedbackPrimitiveTypeCase::getFragmentSource (void) const
   1411 {
   1412 	return specializeShader(s_whiteOutputFragmentShader, m_context.getRenderContext().getType());
   1413 }
   1414 
   1415 std::string FeedbackPrimitiveTypeCase::getTessellationControlSource (void) const
   1416 {
   1417 	std::ostringstream buf;
   1418 
   1419 	buf <<	"${VERSION_DECL}\n"
   1420 			"${EXTENSION_TESSELATION_SHADER}"
   1421 			"layout(vertices = 9) out;\n"
   1422 			"\n"
   1423 			"uniform highp float u_innerTessellationLevel;\n"
   1424 			"uniform highp float u_outerTessellationLevel;\n"
   1425 			"\n"
   1426 			"void main (void)\n"
   1427 			"{\n"
   1428 			"	if (gl_PatchVerticesIn != 4)\n"
   1429 			"		return;\n"
   1430 			"\n"
   1431 			"	// Convert input 2x2 grid to 3x3 grid\n"
   1432 			"	float xweight = float(gl_InvocationID % 3) / 2.0f;\n"
   1433 			"	float yweight = float(gl_InvocationID / 3) / 2.0f;\n"
   1434 			"\n"
   1435 			"	vec4 y0 = mix(gl_in[0].gl_Position, gl_in[1].gl_Position, yweight);\n"
   1436 			"	vec4 y1 = mix(gl_in[2].gl_Position, gl_in[3].gl_Position, yweight);\n"
   1437 			"\n"
   1438 			"	gl_out[gl_InvocationID].gl_Position = mix(y0, y1, xweight);\n"
   1439 			"\n";
   1440 
   1441 	if (m_tessellationOutput == TESSELLATION_OUT_TRIANGLES)
   1442 		buf <<	"	gl_TessLevelOuter[0] = 3.0;\n"
   1443 				"	gl_TessLevelOuter[1] = 3.0;\n"
   1444 				"	gl_TessLevelOuter[2] = 3.0;\n"
   1445 				"	gl_TessLevelInner[0] = 3.0;\n";
   1446 	else if (m_tessellationOutput == TESSELLATION_OUT_QUADS)
   1447 		buf <<	"	gl_TessLevelOuter[0] = 3.0;\n"
   1448 				"	gl_TessLevelOuter[1] = 3.0;\n"
   1449 				"	gl_TessLevelOuter[2] = 3.0;\n"
   1450 				"	gl_TessLevelOuter[3] = 3.0;\n"
   1451 				"	gl_TessLevelInner[0] = 3.0;\n"
   1452 				"	gl_TessLevelInner[1] = 3.0;\n";
   1453 	else if (m_tessellationOutput == TESSELLATION_OUT_ISOLINES)
   1454 		buf <<	"	gl_TessLevelOuter[0] = 3.0;\n"
   1455 				"	gl_TessLevelOuter[1] = 3.0;\n";
   1456 	else
   1457 		DE_ASSERT(false);
   1458 
   1459 	buf <<	"}\n";
   1460 
   1461 	return specializeShader(buf.str(), m_context.getRenderContext().getType());
   1462 }
   1463 
   1464 std::string FeedbackPrimitiveTypeCase::getTessellationEvaluationSource (void) const
   1465 {
   1466 	std::ostringstream buf;
   1467 
   1468 	buf <<	"${VERSION_DECL}\n"
   1469 			"${EXTENSION_TESSELATION_SHADER}"
   1470 			"layout("
   1471 				<< ((m_tessellationOutput == TESSELLATION_OUT_TRIANGLES) ? ("triangles") : (m_tessellationOutput == TESSELLATION_OUT_QUADS) ? ("quads") : ("isolines"))
   1472 				<< ((m_tessellationPointMode) ? (", point_mode") : (""))
   1473 				<< ") in;\n"
   1474 			"\n"
   1475 			"out highp vec4 v_tessellationCoords;\n"
   1476 			"\n"
   1477 			"// note: No need to use precise gl_Position since we do not require gapless geometry\n"
   1478 			"void main (void)\n"
   1479 			"{\n"
   1480 			"	if (gl_PatchVerticesIn != 9)\n"
   1481 			"		return;\n"
   1482 			"\n"
   1483 			"	vec4 patchCentroid = vec4(0.0);\n"
   1484 			"	for (int ndx = 0; ndx < gl_PatchVerticesIn; ++ndx)\n"
   1485 			"		patchCentroid += gl_in[ndx].gl_Position;\n"
   1486 			"	patchCentroid /= patchCentroid.w;\n"
   1487 			"\n";
   1488 
   1489 	if (m_tessellationOutput == TESSELLATION_OUT_TRIANGLES)
   1490 		buf <<	"	// map barycentric coords to 2d coords\n"
   1491 				"	const vec3 tessDirX = vec3( 0.4,  0.4, 0.0);\n"
   1492 				"	const vec3 tessDirY = vec3( 0.0, -0.4, 0.0);\n"
   1493 				"	const vec3 tessDirZ = vec3(-0.4,  0.4, 0.0);\n"
   1494 				"	gl_Position = patchCentroid + vec4(gl_TessCoord.x * tessDirX + gl_TessCoord.y * tessDirY + gl_TessCoord.z * tessDirZ, 0.0);\n";
   1495 	else if (m_tessellationOutput == TESSELLATION_OUT_QUADS || m_tessellationOutput == TESSELLATION_OUT_ISOLINES)
   1496 		buf <<	"	gl_Position = patchCentroid + vec4(gl_TessCoord.x * 0.8 - 0.4, gl_TessCoord.y * 0.8 - 0.4, 0.0, 0.0);\n";
   1497 	else
   1498 		DE_ASSERT(false);
   1499 
   1500 	buf <<	"	v_tessellationCoords = vec4(gl_TessCoord, 0.0);\n"
   1501 			"}\n";
   1502 
   1503 	return specializeShader(buf.str(), m_context.getRenderContext().getType());
   1504 }
   1505 
   1506 std::string FeedbackPrimitiveTypeCase::getGeometrySource (void) const
   1507 {
   1508 	const char* const	geometryInputPrimitive			= (m_tessellationPointMode) ? ("points") : (m_tessellationOutput == TESSELLATION_OUT_ISOLINES) ? ("lines") : ("triangles");
   1509 	const char* const	geometryOutputPrimitive			= (m_geometryOutputType == GEOMETRY_OUTPUT_POINTS) ? ("points") : (m_geometryOutputType == GEOMETRY_OUTPUT_LINES) ? ("line_strip") : ("triangle_strip");
   1510 	const int			numInputVertices				= (m_tessellationPointMode) ? (1) : (m_tessellationOutput == TESSELLATION_OUT_ISOLINES) ? (2) : (3);
   1511 	const int			numSingleVertexOutputVertices	= (m_geometryOutputType == GEOMETRY_OUTPUT_POINTS) ? (1) : (m_geometryOutputType == GEOMETRY_OUTPUT_LINES) ? (4) : (3);
   1512 	const int			numEmitVertices					= numInputVertices * numSingleVertexOutputVertices;
   1513 	std::ostringstream	buf;
   1514 
   1515 	buf <<	"${VERSION_DECL}\n"
   1516 			"${EXTENSION_GEOMETRY_SHADER}"
   1517 			"layout(" << geometryInputPrimitive << ") in;\n"
   1518 			"layout(" << geometryOutputPrimitive << ", max_vertices=" << numEmitVertices <<") out;\n"
   1519 			"\n"
   1520 			"in highp vec4 v_tessellationCoords[];\n"
   1521 			"out highp vec4 tf_someVertexPosition;\n"
   1522 			"\n"
   1523 			"void main (void)\n"
   1524 			"{\n"
   1525 			"	// Emit primitive\n"
   1526 			"	for (int ndx = 0; ndx < gl_in.length(); ++ndx)\n"
   1527 			"	{\n";
   1528 
   1529 	switch (m_geometryOutputType)
   1530 	{
   1531 		case GEOMETRY_OUTPUT_POINTS:
   1532 			buf <<	"		// Draw point on vertex\n"
   1533 					"		gl_Position = gl_in[ndx].gl_Position;\n"
   1534 					"		tf_someVertexPosition = gl_in[gl_in.length() - 1].gl_Position;\n"
   1535 					"		EmitVertex();\n";
   1536 			break;
   1537 
   1538 		case GEOMETRY_OUTPUT_LINES:
   1539 			buf <<	"		// Draw cross on vertex\n"
   1540 					"		gl_Position = gl_in[ndx].gl_Position + vec4(-0.02, -0.02, 0.0, 0.0);\n"
   1541 					"		tf_someVertexPosition = gl_in[gl_in.length() - 1].gl_Position;\n"
   1542 					"		EmitVertex();\n"
   1543 					"		gl_Position = gl_in[ndx].gl_Position + vec4( 0.02,  0.02, 0.0, 0.0);\n"
   1544 					"		tf_someVertexPosition = gl_in[gl_in.length() - 1].gl_Position;\n"
   1545 					"		EmitVertex();\n"
   1546 					"		EndPrimitive();\n"
   1547 					"		gl_Position = gl_in[ndx].gl_Position + vec4( 0.02, -0.02, 0.0, 0.0);\n"
   1548 					"		tf_someVertexPosition = gl_in[gl_in.length() - 1].gl_Position;\n"
   1549 					"		EmitVertex();\n"
   1550 					"		gl_Position = gl_in[ndx].gl_Position + vec4(-0.02,  0.02, 0.0, 0.0);\n"
   1551 					"		tf_someVertexPosition = gl_in[gl_in.length() - 1].gl_Position;\n"
   1552 					"		EmitVertex();\n"
   1553 					"		EndPrimitive();\n";
   1554 			break;
   1555 
   1556 		case GEOMETRY_OUTPUT_TRIANGLES:
   1557 			buf <<	"		// Draw triangle on vertex\n"
   1558 					"		gl_Position = gl_in[ndx].gl_Position + vec4(  0.00, -0.02, 0.0, 0.0);\n"
   1559 					"		tf_someVertexPosition = gl_in[gl_in.length() - 1].gl_Position;\n"
   1560 					"		EmitVertex();\n"
   1561 					"		gl_Position = gl_in[ndx].gl_Position + vec4(  0.02,  0.00, 0.0, 0.0);\n"
   1562 					"		tf_someVertexPosition = gl_in[gl_in.length() - 1].gl_Position;\n"
   1563 					"		EmitVertex();\n"
   1564 					"		gl_Position = gl_in[ndx].gl_Position + vec4( -0.02,  0.00, 0.0, 0.0);\n"
   1565 					"		tf_someVertexPosition = gl_in[gl_in.length() - 1].gl_Position;\n"
   1566 					"		EmitVertex();\n"
   1567 					"		EndPrimitive();\n";
   1568 			break;
   1569 
   1570 		default:
   1571 			DE_ASSERT(false);
   1572 			return "";
   1573 	}
   1574 
   1575 	buf <<	"	}\n"
   1576 			"}\n";
   1577 
   1578 	return specializeShader(buf.str(), m_context.getRenderContext().getType());
   1579 }
   1580 
   1581 const char* FeedbackPrimitiveTypeCase::getTessellationOutputDescription (TessellationOutputType tessellationOutput, TessellationPointMode pointMode)
   1582 {
   1583 	switch (tessellationOutput)
   1584 	{
   1585 		case TESSELLATION_OUT_TRIANGLES:	return (pointMode) ? ("points (triangles in point mode)") : ("triangles");
   1586 		case TESSELLATION_OUT_QUADS:		return (pointMode) ? ("points (quads in point mode)")     : ("quads");
   1587 		case TESSELLATION_OUT_ISOLINES:		return (pointMode) ? ("points (isolines in point mode)")  : ("isolines");
   1588 		default:
   1589 			DE_ASSERT(false);
   1590 			return DE_NULL;
   1591 	}
   1592 }
   1593 
   1594 const char* FeedbackPrimitiveTypeCase::getGeometryInputDescription (TessellationOutputType tessellationOutput, TessellationPointMode pointMode)
   1595 {
   1596 	switch (tessellationOutput)
   1597 	{
   1598 		case TESSELLATION_OUT_TRIANGLES:	return (pointMode) ? ("points") : ("triangles");
   1599 		case TESSELLATION_OUT_QUADS:		return (pointMode) ? ("points") : ("triangles");
   1600 		case TESSELLATION_OUT_ISOLINES:		return (pointMode) ? ("points") : ("lines");
   1601 		default:
   1602 			DE_ASSERT(false);
   1603 			return DE_NULL;
   1604 	}
   1605 }
   1606 
   1607 const char* FeedbackPrimitiveTypeCase::getGeometryOutputDescription (GeometryOutputType geometryOutput)
   1608 {
   1609 	switch (geometryOutput)
   1610 	{
   1611 		case GEOMETRY_OUTPUT_POINTS:		return "points";
   1612 		case GEOMETRY_OUTPUT_LINES:			return "lines";
   1613 		case GEOMETRY_OUTPUT_TRIANGLES:		return "triangles";
   1614 		default:
   1615 			DE_ASSERT(false);
   1616 			return DE_NULL;
   1617 	}
   1618 }
   1619 
   1620 class PointSizeCase : public TestCase
   1621 {
   1622 public:
   1623 	enum Flags
   1624 	{
   1625 		FLAG_VERTEX_SET						= 0x01,		// !< set gl_PointSize in vertex shader
   1626 		FLAG_TESSELLATION_CONTROL_SET		= 0x02,		// !< set gl_PointSize in tessellation evaluation shader
   1627 		FLAG_TESSELLATION_EVALUATION_SET	= 0x04,		// !< set gl_PointSize in tessellation control shader
   1628 		FLAG_TESSELLATION_ADD				= 0x08,		// !< read and add to gl_PointSize in tessellation shader pair
   1629 		FLAG_TESSELLATION_DONT_SET			= 0x10,		// !< don't set gl_PointSize in tessellation shader
   1630 		FLAG_GEOMETRY_SET					= 0x20,		// !< set gl_PointSize in geometry shader
   1631 		FLAG_GEOMETRY_ADD					= 0x40,		// !< read and add to gl_PointSize in geometry shader
   1632 		FLAG_GEOMETRY_DONT_SET				= 0x80,		// !< don't set gl_PointSize in geometry shader
   1633 	};
   1634 
   1635 						PointSizeCase					(Context& context, const char* name, const char* description, int flags);
   1636 						~PointSizeCase					(void);
   1637 
   1638 	static std::string	genTestCaseName					(int flags);
   1639 	static std::string	genTestCaseDescription			(int flags);
   1640 
   1641 private:
   1642 	void				init							(void);
   1643 	void				deinit							(void);
   1644 	IterateResult		iterate							(void);
   1645 
   1646 	void				checkExtensions					(void) const;
   1647 	void				checkPointSizeRequirements		(void) const;
   1648 
   1649 	void				renderTo						(tcu::Surface& dst);
   1650 	bool				verifyImage						(const tcu::Surface& src);
   1651 	int					getExpectedPointSize			(void) const;
   1652 
   1653 	std::string			genVertexSource					(void) const;
   1654 	std::string			genFragmentSource				(void) const;
   1655 	std::string			genTessellationControlSource	(void) const;
   1656 	std::string			genTessellationEvaluationSource	(void) const;
   1657 	std::string			genGeometrySource				(void) const;
   1658 
   1659 	enum
   1660 	{
   1661 		RENDER_SIZE = 32,
   1662 	};
   1663 
   1664 	const int			m_flags;
   1665 	glu::ShaderProgram*	m_program;
   1666 };
   1667 
   1668 PointSizeCase::PointSizeCase (Context& context, const char* name, const char* description, int flags)
   1669 	: TestCase	(context, name, description)
   1670 	, m_flags	(flags)
   1671 	, m_program	(DE_NULL)
   1672 {
   1673 }
   1674 
   1675 PointSizeCase::~PointSizeCase (void)
   1676 {
   1677 	deinit();
   1678 }
   1679 
   1680 std::string PointSizeCase::genTestCaseName (int flags)
   1681 {
   1682 	std::ostringstream buf;
   1683 
   1684 	// join per-bit descriptions into a single string with '_' separator
   1685 	if (flags & FLAG_VERTEX_SET)					buf																		<< "vertex_set";
   1686 	if (flags & FLAG_TESSELLATION_CONTROL_SET)		buf << ((flags & (FLAG_TESSELLATION_CONTROL_SET-1))		? ("_") : (""))	<< "control_set";
   1687 	if (flags & FLAG_TESSELLATION_EVALUATION_SET)	buf << ((flags & (FLAG_TESSELLATION_EVALUATION_SET-1))	? ("_") : (""))	<< "evaluation_set";
   1688 	if (flags & FLAG_TESSELLATION_ADD)				buf << ((flags & (FLAG_TESSELLATION_ADD-1))				? ("_") : (""))	<< "control_pass_eval_add";
   1689 	if (flags & FLAG_TESSELLATION_DONT_SET)			buf << ((flags & (FLAG_TESSELLATION_DONT_SET-1))		? ("_") : (""))	<< "eval_default";
   1690 	if (flags & FLAG_GEOMETRY_SET)					buf << ((flags & (FLAG_GEOMETRY_SET-1))					? ("_") : (""))	<< "geometry_set";
   1691 	if (flags & FLAG_GEOMETRY_ADD)					buf << ((flags & (FLAG_GEOMETRY_ADD-1))					? ("_") : (""))	<< "geometry_add";
   1692 	if (flags & FLAG_GEOMETRY_DONT_SET)				buf << ((flags & (FLAG_GEOMETRY_DONT_SET-1))			? ("_") : (""))	<< "geometry_default";
   1693 
   1694 	return buf.str();
   1695 }
   1696 
   1697 std::string PointSizeCase::genTestCaseDescription (int flags)
   1698 {
   1699 	std::ostringstream buf;
   1700 
   1701 	// join per-bit descriptions into a single string with ", " separator
   1702 	if (flags & FLAG_VERTEX_SET)					buf																			<< "set point size in vertex shader";
   1703 	if (flags & FLAG_TESSELLATION_CONTROL_SET)		buf << ((flags & (FLAG_TESSELLATION_CONTROL_SET-1))		? (", ") : (""))	<< "set point size in tessellation control shader";
   1704 	if (flags & FLAG_TESSELLATION_EVALUATION_SET)	buf << ((flags & (FLAG_TESSELLATION_EVALUATION_SET-1))	? (", ") : (""))	<< "set point size in tessellation evaluation shader";
   1705 	if (flags & FLAG_TESSELLATION_ADD)				buf << ((flags & (FLAG_TESSELLATION_ADD-1))				? (", ") : (""))	<< "add to point size in tessellation shader";
   1706 	if (flags & FLAG_TESSELLATION_DONT_SET)			buf << ((flags & (FLAG_TESSELLATION_DONT_SET-1))		? (", ") : (""))	<< "don't set point size in tessellation evaluation shader";
   1707 	if (flags & FLAG_GEOMETRY_SET)					buf << ((flags & (FLAG_GEOMETRY_SET-1))					? (", ") : (""))	<< "set point size in geometry shader";
   1708 	if (flags & FLAG_GEOMETRY_ADD)					buf << ((flags & (FLAG_GEOMETRY_ADD-1))					? (", ") : (""))	<< "add to point size in geometry shader";
   1709 	if (flags & FLAG_GEOMETRY_DONT_SET)				buf << ((flags & (FLAG_GEOMETRY_DONT_SET-1))			? (", ") : (""))	<< "don't set point size in geometry shader";
   1710 
   1711 	return buf.str();
   1712 }
   1713 
   1714 void PointSizeCase::init (void)
   1715 {
   1716 	checkExtensions();
   1717 	checkPointSizeRequirements();
   1718 
   1719 	// log
   1720 
   1721 	if (m_flags & FLAG_VERTEX_SET)
   1722 		m_testCtx.getLog() << tcu::TestLog::Message << "Setting point size in vertex shader to 2.0." << tcu::TestLog::EndMessage;
   1723 	if (m_flags & FLAG_TESSELLATION_CONTROL_SET)
   1724 		m_testCtx.getLog() << tcu::TestLog::Message << "Setting point size in tessellation control shader to 4.0. (And ignoring it in evaluation)." << tcu::TestLog::EndMessage;
   1725 	if (m_flags & FLAG_TESSELLATION_EVALUATION_SET)
   1726 		m_testCtx.getLog() << tcu::TestLog::Message << "Setting point size in tessellation evaluation shader to 4.0." << tcu::TestLog::EndMessage;
   1727 	if (m_flags & FLAG_TESSELLATION_ADD)
   1728 		m_testCtx.getLog() << tcu::TestLog::Message << "Reading point size in tessellation control shader and adding 2.0 to it in evaluation." << tcu::TestLog::EndMessage;
   1729 	if (m_flags & FLAG_TESSELLATION_DONT_SET)
   1730 		m_testCtx.getLog() << tcu::TestLog::Message << "Not setting point size in tessellation evaluation shader (resulting in the default point size)." << tcu::TestLog::EndMessage;
   1731 	if (m_flags & FLAG_GEOMETRY_SET)
   1732 		m_testCtx.getLog() << tcu::TestLog::Message << "Setting point size in geometry shader to 6.0." << tcu::TestLog::EndMessage;
   1733 	if (m_flags & FLAG_GEOMETRY_ADD)
   1734 		m_testCtx.getLog() << tcu::TestLog::Message << "Reading point size in geometry shader and adding 2.0." << tcu::TestLog::EndMessage;
   1735 	if (m_flags & FLAG_GEOMETRY_DONT_SET)
   1736 		m_testCtx.getLog() << tcu::TestLog::Message << "Not setting point size in geometry shader (resulting in the default point size)." << tcu::TestLog::EndMessage;
   1737 
   1738 	// program
   1739 
   1740 	{
   1741 		glu::ProgramSources sources;
   1742 		sources	<< glu::VertexSource(genVertexSource())
   1743 				<< glu::FragmentSource(genFragmentSource());
   1744 
   1745 		if (m_flags & (FLAG_TESSELLATION_CONTROL_SET | FLAG_TESSELLATION_EVALUATION_SET | FLAG_TESSELLATION_ADD | FLAG_TESSELLATION_DONT_SET))
   1746 			sources << glu::TessellationControlSource(genTessellationControlSource())
   1747 					<< glu::TessellationEvaluationSource(genTessellationEvaluationSource());
   1748 
   1749 		if (m_flags & (FLAG_GEOMETRY_SET | FLAG_GEOMETRY_ADD | FLAG_GEOMETRY_DONT_SET))
   1750 			sources << glu::GeometrySource(genGeometrySource());
   1751 
   1752 		m_program = new glu::ShaderProgram(m_context.getRenderContext(), sources);
   1753 
   1754 		m_testCtx.getLog() << *m_program;
   1755 		if (!m_program->isOk())
   1756 			throw tcu::TestError("failed to build program");
   1757 	}
   1758 }
   1759 
   1760 void PointSizeCase::deinit (void)
   1761 {
   1762 	delete m_program;
   1763 	m_program = DE_NULL;
   1764 }
   1765 
   1766 PointSizeCase::IterateResult PointSizeCase::iterate (void)
   1767 {
   1768 	tcu::Surface resultImage(RENDER_SIZE, RENDER_SIZE);
   1769 
   1770 	renderTo(resultImage);
   1771 
   1772 	if (verifyImage(resultImage))
   1773 		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
   1774 	else
   1775 		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
   1776 
   1777 	return STOP;
   1778 }
   1779 
   1780 void PointSizeCase::checkExtensions (void) const
   1781 {
   1782 	std::vector<std::string>	requiredExtensions;
   1783 	const bool					supportsES32		= glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2));
   1784 	bool						allOk				= true;
   1785 
   1786 	if ((m_flags & (FLAG_TESSELLATION_CONTROL_SET | FLAG_TESSELLATION_EVALUATION_SET | FLAG_TESSELLATION_ADD | FLAG_TESSELLATION_DONT_SET)) && !supportsES32)
   1787 		requiredExtensions.push_back("GL_EXT_tessellation_shader");
   1788 
   1789 	if (m_flags & (FLAG_TESSELLATION_CONTROL_SET | FLAG_TESSELLATION_EVALUATION_SET | FLAG_TESSELLATION_ADD))
   1790 		requiredExtensions.push_back("GL_EXT_tessellation_point_size");
   1791 
   1792 	if ((m_flags & (m_flags & (FLAG_GEOMETRY_SET | FLAG_GEOMETRY_ADD | FLAG_GEOMETRY_DONT_SET))) && !supportsES32)
   1793 		requiredExtensions.push_back("GL_EXT_geometry_shader");
   1794 
   1795 	if (m_flags & (m_flags & (FLAG_GEOMETRY_SET | FLAG_GEOMETRY_ADD)))
   1796 		requiredExtensions.push_back("GL_EXT_geometry_point_size");
   1797 
   1798 	for (int ndx = 0; ndx < (int)requiredExtensions.size(); ++ndx)
   1799 		if (!m_context.getContextInfo().isExtensionSupported(requiredExtensions[ndx].c_str()))
   1800 			allOk = false;
   1801 
   1802 	if (!allOk)
   1803 	{
   1804 		std::ostringstream extensionList;
   1805 
   1806 		for (int ndx = 0; ndx < (int)requiredExtensions.size(); ++ndx)
   1807 		{
   1808 			if (ndx != 0)
   1809 				extensionList << ", ";
   1810 			extensionList << requiredExtensions[ndx];
   1811 		}
   1812 
   1813 		throw tcu::NotSupportedError("Test requires {" + extensionList.str() + "} extension(s)");
   1814 	}
   1815 }
   1816 
   1817 void PointSizeCase::checkPointSizeRequirements (void) const
   1818 {
   1819 	const glw::Functions&	gl					= m_context.getRenderContext().getFunctions();
   1820 	float					aliasedSizeRange[2]	= { 0.0f, 0.0f };
   1821 	const int				requiredSize		= getExpectedPointSize();
   1822 
   1823 	gl.getFloatv(GL_ALIASED_POINT_SIZE_RANGE, aliasedSizeRange);
   1824 
   1825 	if (float(requiredSize) > aliasedSizeRange[1])
   1826 		throw tcu::NotSupportedError("Test requires point size " + de::toString(requiredSize));
   1827 }
   1828 
   1829 void PointSizeCase::renderTo (tcu::Surface& dst)
   1830 {
   1831 	const glw::Functions&	gl					= m_context.getRenderContext().getFunctions();
   1832 	const bool				tessellationActive	= (m_flags & (FLAG_TESSELLATION_CONTROL_SET | FLAG_TESSELLATION_EVALUATION_SET | FLAG_TESSELLATION_ADD | FLAG_TESSELLATION_DONT_SET)) != 0;
   1833 	const int				positionLocation	= gl.getAttribLocation(m_program->getProgram(), "a_position");
   1834 	const glu::VertexArray	vao					(m_context.getRenderContext());
   1835 
   1836 	m_testCtx.getLog() << tcu::TestLog::Message << "Rendering single point." << tcu::TestLog::EndMessage;
   1837 
   1838 	if (positionLocation == -1)
   1839 		throw tcu::TestError("Attribute a_position location was -1");
   1840 
   1841 	gl.viewport(0, 0, RENDER_SIZE, RENDER_SIZE);
   1842 	gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
   1843 	gl.clear(GL_COLOR_BUFFER_BIT);
   1844 	GLU_EXPECT_NO_ERROR(gl.getError(), "clear");
   1845 
   1846 	gl.bindVertexArray(*vao);
   1847 	GLU_EXPECT_NO_ERROR(gl.getError(), "bind vao");
   1848 
   1849 	gl.useProgram(m_program->getProgram());
   1850 	GLU_EXPECT_NO_ERROR(gl.getError(), "use program");
   1851 
   1852 	gl.vertexAttrib4f(positionLocation, 0.0f, 0.0f, 0.0f, 1.0f);
   1853 
   1854 	if (tessellationActive)
   1855 	{
   1856 		gl.patchParameteri(GL_PATCH_VERTICES, 1);
   1857 		GLU_EXPECT_NO_ERROR(gl.getError(), "set patch param");
   1858 
   1859 		gl.drawArrays(GL_PATCHES, 0, 1);
   1860 		GLU_EXPECT_NO_ERROR(gl.getError(), "draw patches");
   1861 	}
   1862 	else
   1863 	{
   1864 		gl.drawArrays(GL_POINTS, 0, 1);
   1865 		GLU_EXPECT_NO_ERROR(gl.getError(), "draw points");
   1866 	}
   1867 
   1868 	glu::readPixels(m_context.getRenderContext(), 0, 0, dst.getAccess());
   1869 }
   1870 
   1871 bool PointSizeCase::verifyImage (const tcu::Surface& src)
   1872 {
   1873 	const bool MSAATarget	= (m_context.getRenderTarget().getNumSamples() > 1);
   1874 	const int expectedSize	= getExpectedPointSize();
   1875 
   1876 	m_testCtx.getLog() << tcu::TestLog::Message << "Verifying rendered point size. Expecting " << expectedSize << " pixels." << tcu::TestLog::EndMessage;
   1877 	m_testCtx.getLog() << tcu::TestLog::Image("RenderImage", "Rendered image", src.getAccess());
   1878 
   1879 	{
   1880 		bool		resultAreaFound	= false;
   1881 		tcu::IVec4	resultArea;
   1882 
   1883 		// Find rasterization output area
   1884 
   1885 		for (int y = 0; y < src.getHeight(); ++y)
   1886 		for (int x = 0; x < src.getWidth();  ++x)
   1887 		{
   1888 			if (!isBlack(src.getPixel(x, y)))
   1889 			{
   1890 				if (!resultAreaFound)
   1891 				{
   1892 					// first fragment
   1893 					resultArea = tcu::IVec4(x, y, x + 1, y + 1);
   1894 					resultAreaFound = true;
   1895 				}
   1896 				else
   1897 				{
   1898 					// union area
   1899 					resultArea.x() = de::min(resultArea.x(), x);
   1900 					resultArea.y() = de::min(resultArea.y(), y);
   1901 					resultArea.z() = de::max(resultArea.z(), x+1);
   1902 					resultArea.w() = de::max(resultArea.w(), y+1);
   1903 				}
   1904 			}
   1905 		}
   1906 
   1907 		if (!resultAreaFound)
   1908 		{
   1909 			m_testCtx.getLog() << tcu::TestLog::Message << "Verification failed, could not find any point fragments." << tcu::TestLog::EndMessage;
   1910 			return false;
   1911 		}
   1912 
   1913 		// verify area size
   1914 		if (MSAATarget)
   1915 		{
   1916 			const tcu::IVec2 pointSize = resultArea.swizzle(2,3) - resultArea.swizzle(0, 1);
   1917 
   1918 			// MSAA: edges may be a little fuzzy
   1919 			if (de::abs(pointSize.x() - pointSize.y()) > 1)
   1920 			{
   1921 				m_testCtx.getLog() << tcu::TestLog::Message << "ERROR! Rasterized point is not a square. Detected point size was " << pointSize << tcu::TestLog::EndMessage;
   1922 				return false;
   1923 			}
   1924 
   1925 			// MSAA may produce larger areas, allow one pixel larger
   1926 			if (expectedSize != de::max(pointSize.x(), pointSize.y()) && (expectedSize+1) != de::max(pointSize.x(), pointSize.y()))
   1927 			{
   1928 				m_testCtx.getLog() << tcu::TestLog::Message << "ERROR! Point size invalid, expected " << expectedSize << ", got " << de::max(pointSize.x(), pointSize.y()) << tcu::TestLog::EndMessage;
   1929 				return false;
   1930 			}
   1931 		}
   1932 		else
   1933 		{
   1934 			const tcu::IVec2 pointSize = resultArea.swizzle(2,3) - resultArea.swizzle(0, 1);
   1935 
   1936 			if (pointSize.x() != pointSize.y())
   1937 			{
   1938 				m_testCtx.getLog() << tcu::TestLog::Message << "ERROR! Rasterized point is not a square. Point size was " << pointSize << tcu::TestLog::EndMessage;
   1939 				return false;
   1940 			}
   1941 
   1942 			if (pointSize.x() != expectedSize)
   1943 			{
   1944 				m_testCtx.getLog() << tcu::TestLog::Message << "ERROR! Point size invalid, expected " << expectedSize << ", got " << pointSize.x() << tcu::TestLog::EndMessage;
   1945 				return false;
   1946 			}
   1947 		}
   1948 	}
   1949 
   1950 	return true;
   1951 }
   1952 
   1953 int PointSizeCase::getExpectedPointSize (void) const
   1954 {
   1955 	int addition = 0;
   1956 
   1957 	// geometry
   1958 	if (m_flags & FLAG_GEOMETRY_DONT_SET)
   1959 		return 1;
   1960 	else if (m_flags & FLAG_GEOMETRY_SET)
   1961 		return 6;
   1962 	else if (m_flags & FLAG_GEOMETRY_ADD)
   1963 		addition += 2;
   1964 
   1965 	// tessellation
   1966 	if (m_flags & FLAG_TESSELLATION_EVALUATION_SET)
   1967 		return 4 + addition;
   1968 	else if (m_flags & FLAG_TESSELLATION_ADD)
   1969 		addition += 2;
   1970 	else if (m_flags & (FLAG_TESSELLATION_CONTROL_SET | FLAG_TESSELLATION_DONT_SET))
   1971 	{
   1972 		DE_ASSERT((m_flags & FLAG_GEOMETRY_ADD) == 0); // reading pointSize undefined
   1973 		return 1;
   1974 	}
   1975 
   1976 	// vertex
   1977 	if (m_flags & FLAG_VERTEX_SET)
   1978 		return 2 + addition;
   1979 
   1980 	// undefined
   1981 	DE_ASSERT(false);
   1982 	return -1;
   1983 }
   1984 
   1985 std::string PointSizeCase::genVertexSource (void) const
   1986 {
   1987 	std::ostringstream buf;
   1988 
   1989 	buf	<< "${VERSION_DECL}\n"
   1990 		<< "in highp vec4 a_position;\n"
   1991 		<< "void main ()\n"
   1992 		<< "{\n"
   1993 		<< "	gl_Position = a_position;\n";
   1994 
   1995 	if (m_flags & FLAG_VERTEX_SET)
   1996 		buf << "	gl_PointSize = 2.0;\n";
   1997 
   1998 	buf	<< "}\n";
   1999 
   2000 	return specializeShader(buf.str(), m_context.getRenderContext().getType());
   2001 }
   2002 
   2003 std::string PointSizeCase::genFragmentSource (void) const
   2004 {
   2005 	return specializeShader(s_whiteOutputFragmentShader, m_context.getRenderContext().getType());
   2006 }
   2007 
   2008 std::string PointSizeCase::genTessellationControlSource (void) const
   2009 {
   2010 	std::ostringstream buf;
   2011 
   2012 	buf	<< "${VERSION_DECL}\n"
   2013 		<< "${EXTENSION_TESSELATION_SHADER}"
   2014 		<< ((m_flags & FLAG_TESSELLATION_DONT_SET) ? ("") : ("#extension GL_EXT_tessellation_point_size : require\n"))
   2015 		<< "layout(vertices = 1) out;\n"
   2016 		<< "void main ()\n"
   2017 		<< "{\n"
   2018 		<< "	gl_TessLevelOuter[0] = 3.0;\n"
   2019 		<< "	gl_TessLevelOuter[1] = 3.0;\n"
   2020 		<< "	gl_TessLevelOuter[2] = 3.0;\n"
   2021 		<< "	gl_TessLevelInner[0] = 3.0;\n"
   2022 		<< "	gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n";
   2023 
   2024 	if (m_flags & FLAG_TESSELLATION_ADD)
   2025 		buf << "	// pass as is to eval\n"
   2026 			<< "	gl_out[gl_InvocationID].gl_PointSize = gl_in[gl_InvocationID].gl_PointSize;\n";
   2027 	else if (m_flags & FLAG_TESSELLATION_CONTROL_SET)
   2028 		buf << "	// thrown away\n"
   2029 			<< "	gl_out[gl_InvocationID].gl_PointSize = 4.0;\n";
   2030 
   2031 	buf	<< "}\n";
   2032 
   2033 	return specializeShader(buf.str(), m_context.getRenderContext().getType());
   2034 }
   2035 
   2036 std::string PointSizeCase::genTessellationEvaluationSource (void) const
   2037 {
   2038 	std::ostringstream buf;
   2039 
   2040 	buf	<< "${VERSION_DECL}\n"
   2041 		<< "${EXTENSION_TESSELATION_SHADER}"
   2042 		<< ((m_flags & FLAG_TESSELLATION_DONT_SET) ? ("") : ("#extension GL_EXT_tessellation_point_size : require\n"))
   2043 		<< "layout(triangles, point_mode) in;\n"
   2044 		<< "void main ()\n"
   2045 		<< "{\n"
   2046 		<< "	// hide all but one vertex\n"
   2047 		<< "	if (gl_TessCoord.x < 0.99)\n"
   2048 		<< "		gl_Position = vec4(-2.0, 0.0, 0.0, 1.0);\n"
   2049 		<< "	else\n"
   2050 		<< "		gl_Position = gl_in[0].gl_Position;\n";
   2051 
   2052 	if (m_flags & FLAG_TESSELLATION_ADD)
   2053 		buf << "\n"
   2054 			<< "	// add to point size\n"
   2055 			<< "	gl_PointSize = gl_in[0].gl_PointSize + 2.0;\n";
   2056 	else if (m_flags & FLAG_TESSELLATION_EVALUATION_SET)
   2057 		buf << "\n"
   2058 			<< "	// set point size\n"
   2059 			<< "	gl_PointSize = 4.0;\n";
   2060 
   2061 	buf	<< "}\n";
   2062 
   2063 	return specializeShader(buf.str(), m_context.getRenderContext().getType());
   2064 }
   2065 
   2066 std::string PointSizeCase::genGeometrySource (void) const
   2067 {
   2068 	std::ostringstream buf;
   2069 
   2070 	buf	<< "${VERSION_DECL}\n"
   2071 		<< "${EXTENSION_GEOMETRY_SHADER}"
   2072 		<< ((m_flags & FLAG_GEOMETRY_DONT_SET) ? ("") : ("#extension GL_EXT_geometry_point_size : require\n"))
   2073 		<< "layout (points) in;\n"
   2074 		<< "layout (points, max_vertices=1) out;\n"
   2075 		<< "\n"
   2076 		<< "void main ()\n"
   2077 		<< "{\n";
   2078 
   2079 	if (m_flags & FLAG_GEOMETRY_SET)
   2080 		buf	<< "	gl_Position = gl_in[0].gl_Position;\n"
   2081 			<< "	gl_PointSize = 6.0;\n";
   2082 	else if (m_flags & FLAG_GEOMETRY_ADD)
   2083 		buf	<< "	gl_Position = gl_in[0].gl_Position;\n"
   2084 			<< "	gl_PointSize = gl_in[0].gl_PointSize + 2.0;\n";
   2085 	else if (m_flags & FLAG_GEOMETRY_DONT_SET)
   2086 		buf	<< "	gl_Position = gl_in[0].gl_Position;\n";
   2087 
   2088 	buf	<< "	EmitVertex();\n"
   2089 		<< "}\n";
   2090 
   2091 	return specializeShader(buf.str(), m_context.getRenderContext().getType());
   2092 }
   2093 
   2094 class AllowedRenderFailureException : public std::runtime_error
   2095 {
   2096 public:
   2097 	AllowedRenderFailureException (const char* message) : std::runtime_error(message) { }
   2098 };
   2099 
   2100 class GridRenderCase : public TestCase
   2101 {
   2102 public:
   2103 	enum Flags
   2104 	{
   2105 		FLAG_TESSELLATION_MAX_SPEC						= 0x0001,
   2106 		FLAG_TESSELLATION_MAX_IMPLEMENTATION			= 0x0002,
   2107 		FLAG_GEOMETRY_MAX_SPEC							= 0x0004,
   2108 		FLAG_GEOMETRY_MAX_IMPLEMENTATION				= 0x0008,
   2109 		FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC				= 0x0010,
   2110 		FLAG_GEOMETRY_INVOCATIONS_MAX_IMPLEMENTATION	= 0x0020,
   2111 
   2112 		FLAG_GEOMETRY_SCATTER_INSTANCES					= 0x0040,
   2113 		FLAG_GEOMETRY_SCATTER_PRIMITIVES				= 0x0080,
   2114 		FLAG_GEOMETRY_SEPARATE_PRIMITIVES				= 0x0100, //!< if set, geometry shader outputs separate grid cells and not continuous slices
   2115 		FLAG_GEOMETRY_SCATTER_LAYERS					= 0x0200,
   2116 
   2117 		FLAG_ALLOW_OUT_OF_MEMORY						= 0x0400, //!< allow draw command to set GL_OUT_OF_MEMORY
   2118 	};
   2119 
   2120 						GridRenderCase					(Context& context, const char* name, const char* description, int flags);
   2121 						~GridRenderCase					(void);
   2122 
   2123 private:
   2124 	void				init							(void);
   2125 	void				deinit							(void);
   2126 	IterateResult		iterate							(void);
   2127 
   2128 	void				renderTo						(std::vector<tcu::Surface>& dst);
   2129 	bool				verifyResultLayer				(int layerNdx, const tcu::Surface& dst);
   2130 
   2131 	std::string			getVertexSource					(void);
   2132 	std::string			getFragmentSource				(void);
   2133 	std::string			getTessellationControlSource	(int tessLevel);
   2134 	std::string			getTessellationEvaluationSource	(int tessLevel);
   2135 	std::string			getGeometryShaderSource			(int numPrimitives, int numInstances, int tessLevel);
   2136 
   2137 	enum
   2138 	{
   2139 		RENDER_SIZE = 256
   2140 	};
   2141 
   2142 	const int			m_flags;
   2143 
   2144 	glu::ShaderProgram*	m_program;
   2145 	deUint32			m_texture;
   2146 	int					m_numLayers;
   2147 };
   2148 
   2149 GridRenderCase::GridRenderCase (Context& context, const char* name, const char* description, int flags)
   2150 	: TestCase		(context, name, description)
   2151 	, m_flags		(flags)
   2152 	, m_program		(DE_NULL)
   2153 	, m_texture		(0)
   2154 	, m_numLayers	(1)
   2155 {
   2156 	DE_ASSERT(((m_flags & FLAG_TESSELLATION_MAX_SPEC) == 0)			|| ((m_flags & FLAG_TESSELLATION_MAX_IMPLEMENTATION) == 0));
   2157 	DE_ASSERT(((m_flags & FLAG_GEOMETRY_MAX_SPEC) == 0)				|| ((m_flags & FLAG_GEOMETRY_MAX_IMPLEMENTATION) == 0));
   2158 	DE_ASSERT(((m_flags & FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC) == 0)	|| ((m_flags & FLAG_GEOMETRY_INVOCATIONS_MAX_IMPLEMENTATION) == 0));
   2159 	DE_ASSERT(((m_flags & (FLAG_GEOMETRY_SCATTER_PRIMITIVES | FLAG_GEOMETRY_SCATTER_LAYERS)) != 0) == ((m_flags & FLAG_GEOMETRY_SEPARATE_PRIMITIVES) != 0));
   2160 }
   2161 
   2162 GridRenderCase::~GridRenderCase (void)
   2163 {
   2164 	deinit();
   2165 }
   2166 
   2167 void GridRenderCase::init (void)
   2168 {
   2169 	const glw::Functions&	gl				= m_context.getRenderContext().getFunctions();
   2170 	const bool				supportsES32	= glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2));
   2171 
   2172 	// Requirements
   2173 
   2174 	if (!supportsES32 &&
   2175 		(!m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader") ||
   2176 		 !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader")))
   2177 		throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader and GL_EXT_geometry_shader extensions");
   2178 
   2179 	if ((m_flags & FLAG_GEOMETRY_SCATTER_LAYERS) == 0)
   2180 	{
   2181 		if (m_context.getRenderTarget().getWidth() < RENDER_SIZE ||
   2182 			m_context.getRenderTarget().getHeight() < RENDER_SIZE)
   2183 			throw tcu::NotSupportedError("Test requires " + de::toString<int>(RENDER_SIZE) + "x" + de::toString<int>(RENDER_SIZE) + " or larger render target.");
   2184 	}
   2185 
   2186 	// Log
   2187 
   2188 	m_testCtx.getLog()
   2189 		<< tcu::TestLog::Message
   2190 		<< "Testing tessellation and geometry shaders that output a large number of primitives.\n"
   2191 		<< getDescription()
   2192 		<< tcu::TestLog::EndMessage;
   2193 
   2194 	// Render target
   2195 	if (m_flags & FLAG_GEOMETRY_SCATTER_LAYERS)
   2196 	{
   2197 		// set limits
   2198 		m_numLayers = 8;
   2199 
   2200 		m_testCtx.getLog() << tcu::TestLog::Message << "Rendering to 2d texture array, numLayers = " << m_numLayers << tcu::TestLog::EndMessage;
   2201 
   2202 		gl.genTextures(1, &m_texture);
   2203 		gl.bindTexture(GL_TEXTURE_2D_ARRAY, m_texture);
   2204 		gl.texStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_RGBA8, RENDER_SIZE, RENDER_SIZE, m_numLayers);
   2205 
   2206 		gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
   2207 		gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
   2208 		gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
   2209 		gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
   2210 
   2211 		GLU_EXPECT_NO_ERROR(gl.getError(), "gen texture");
   2212 	}
   2213 
   2214 	// Gen program
   2215 	{
   2216 		glu::ProgramSources	sources;
   2217 		int					tessGenLevel = -1;
   2218 
   2219 		sources	<< glu::VertexSource(getVertexSource())
   2220 				<< glu::FragmentSource(getFragmentSource());
   2221 
   2222 		// Tessellation limits
   2223 		{
   2224 			if (m_flags & FLAG_TESSELLATION_MAX_IMPLEMENTATION)
   2225 			{
   2226 				gl.getIntegerv(GL_MAX_TESS_GEN_LEVEL, &tessGenLevel);
   2227 				GLU_EXPECT_NO_ERROR(gl.getError(), "query tessellation limits");
   2228 			}
   2229 			else if (m_flags & FLAG_TESSELLATION_MAX_SPEC)
   2230 			{
   2231 				tessGenLevel = 64;
   2232 			}
   2233 			else
   2234 			{
   2235 				tessGenLevel = 5;
   2236 			}
   2237 
   2238 			m_testCtx.getLog()
   2239 					<< tcu::TestLog::Message
   2240 					<< "Tessellation level: " << tessGenLevel << ", mode = quad.\n"
   2241 					<< "\tEach input patch produces " << (tessGenLevel*tessGenLevel) << " (" << (tessGenLevel*tessGenLevel*2) << " triangles)\n"
   2242 					<< tcu::TestLog::EndMessage;
   2243 
   2244 			sources << glu::TessellationControlSource(getTessellationControlSource(tessGenLevel))
   2245 					<< glu::TessellationEvaluationSource(getTessellationEvaluationSource(tessGenLevel));
   2246 		}
   2247 
   2248 		// Geometry limits
   2249 		{
   2250 			int		geometryOutputComponents		= -1;
   2251 			int		geometryOutputVertices			= -1;
   2252 			int		geometryTotalOutputComponents	= -1;
   2253 			int		geometryShaderInvocations		= -1;
   2254 			bool	logGeometryLimits				= false;
   2255 			bool	logInvocationLimits				= false;
   2256 
   2257 			if (m_flags & FLAG_GEOMETRY_MAX_IMPLEMENTATION)
   2258 			{
   2259 				m_testCtx.getLog() << tcu::TestLog::Message << "Using implementation maximum geometry shader output limits." << tcu::TestLog::EndMessage;
   2260 
   2261 				gl.getIntegerv(GL_MAX_GEOMETRY_OUTPUT_COMPONENTS, &geometryOutputComponents);
   2262 				gl.getIntegerv(GL_MAX_GEOMETRY_OUTPUT_VERTICES, &geometryOutputVertices);
   2263 				gl.getIntegerv(GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS, &geometryTotalOutputComponents);
   2264 				GLU_EXPECT_NO_ERROR(gl.getError(), "query geometry limits");
   2265 
   2266 				logGeometryLimits = true;
   2267 			}
   2268 			else if (m_flags & FLAG_GEOMETRY_MAX_SPEC)
   2269 			{
   2270 				m_testCtx.getLog() << tcu::TestLog::Message << "Using geometry shader extension minimum maximum output limits." << tcu::TestLog::EndMessage;
   2271 
   2272 				geometryOutputComponents = 128;
   2273 				geometryOutputVertices = 256;
   2274 				geometryTotalOutputComponents = 1024;
   2275 				logGeometryLimits = true;
   2276 			}
   2277 			else
   2278 			{
   2279 				geometryOutputComponents = 128;
   2280 				geometryOutputVertices = 16;
   2281 				geometryTotalOutputComponents = 1024;
   2282 			}
   2283 
   2284 			if (m_flags & FLAG_GEOMETRY_INVOCATIONS_MAX_IMPLEMENTATION)
   2285 			{
   2286 				gl.getIntegerv(GL_MAX_GEOMETRY_SHADER_INVOCATIONS, &geometryShaderInvocations);
   2287 				GLU_EXPECT_NO_ERROR(gl.getError(), "query geometry invocation limits");
   2288 
   2289 				logInvocationLimits = true;
   2290 			}
   2291 			else if (m_flags & FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC)
   2292 			{
   2293 				geometryShaderInvocations = 32;
   2294 				logInvocationLimits = true;
   2295 			}
   2296 			else
   2297 			{
   2298 				geometryShaderInvocations = 4;
   2299 			}
   2300 
   2301 			if (logGeometryLimits || logInvocationLimits)
   2302 			{
   2303 				tcu::MessageBuilder msg(&m_testCtx.getLog());
   2304 
   2305 				msg << "Geometry shader, targeting following limits:\n";
   2306 
   2307 				if (logGeometryLimits)
   2308 					msg	<< "\tGL_MAX_GEOMETRY_OUTPUT_COMPONENTS = " << geometryOutputComponents << "\n"
   2309 						<< "\tGL_MAX_GEOMETRY_OUTPUT_VERTICES = " << geometryOutputVertices << "\n"
   2310 						<< "\tGL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS = " << geometryTotalOutputComponents << "\n";
   2311 
   2312 				if (logInvocationLimits)
   2313 					msg << "\tGL_MAX_GEOMETRY_SHADER_INVOCATIONS = " << geometryShaderInvocations;
   2314 
   2315 				msg << tcu::TestLog::EndMessage;
   2316 			}
   2317 
   2318 			{
   2319 				const bool	separatePrimitives			= (m_flags & FLAG_GEOMETRY_SEPARATE_PRIMITIVES) != 0;
   2320 				const int	numComponentsPerVertex		= 8; // vec4 pos, vec4 color
   2321 				int			numVerticesPerInvocation;
   2322 				int			numPrimitivesPerInvocation;
   2323 				int			geometryVerticesPerPrimitive;
   2324 				int			geometryPrimitivesOutPerPrimitive;
   2325 
   2326 				if (separatePrimitives)
   2327 				{
   2328 					const int	numComponentLimit	= geometryTotalOutputComponents / (4 * numComponentsPerVertex);
   2329 					const int	numOutputLimit		= geometryOutputVertices / 4;
   2330 
   2331 					numPrimitivesPerInvocation		= de::min(numComponentLimit, numOutputLimit);
   2332 					numVerticesPerInvocation		= numPrimitivesPerInvocation * 4;
   2333 				}
   2334 				else
   2335 				{
   2336 					// If FLAG_GEOMETRY_SEPARATE_PRIMITIVES is not set, geometry shader fills a rectangle area in slices.
   2337 					// Each slice is a triangle strip and is generated by a single shader invocation.
   2338 					// One slice with 4 segment ends (nodes) and 3 segments:
   2339 					//    .__.__.__.
   2340 					//    |\ |\ |\ |
   2341 					//    |_\|_\|_\|
   2342 
   2343 					const int	numSliceNodesComponentLimit	= geometryTotalOutputComponents / (2 * numComponentsPerVertex);			// each node 2 vertices
   2344 					const int	numSliceNodesOutputLimit	= geometryOutputVertices / 2;											// each node 2 vertices
   2345 					const int	numSliceNodes				= de::min(numSliceNodesComponentLimit, numSliceNodesOutputLimit);
   2346 
   2347 					numVerticesPerInvocation				= numSliceNodes * 2;
   2348 					numPrimitivesPerInvocation				= (numSliceNodes - 1) * 2;
   2349 				}
   2350 
   2351 				geometryVerticesPerPrimitive = numVerticesPerInvocation * geometryShaderInvocations;
   2352 				geometryPrimitivesOutPerPrimitive = numPrimitivesPerInvocation * geometryShaderInvocations;
   2353 
   2354 				m_testCtx.getLog()
   2355 					<< tcu::TestLog::Message
   2356 					<< "Geometry shader:\n"
   2357 					<< "\tTotal output vertex count per invocation: " << (numVerticesPerInvocation) << "\n"
   2358 					<< "\tTotal output primitive count per invocation: " << (numPrimitivesPerInvocation) << "\n"
   2359 					<< "\tNumber of invocations per primitive: " << geometryShaderInvocations << "\n"
   2360 					<< "\tTotal output vertex count per input primitive: " << (geometryVerticesPerPrimitive) << "\n"
   2361 					<< "\tTotal output primitive count per input primitive: " << (geometryPrimitivesOutPerPrimitive) << "\n"
   2362 					<< tcu::TestLog::EndMessage;
   2363 
   2364 				sources	<< glu::GeometrySource(getGeometryShaderSource(numPrimitivesPerInvocation, geometryShaderInvocations, tessGenLevel));
   2365 
   2366 				m_testCtx.getLog()
   2367 					<< tcu::TestLog::Message
   2368 					<< "Program:\n"
   2369 					<< "\tTotal program output vertices count per input patch: " << (tessGenLevel*tessGenLevel*2 * geometryVerticesPerPrimitive) << "\n"
   2370 					<< "\tTotal program output primitive count per input patch: " << (tessGenLevel*tessGenLevel*2 * geometryPrimitivesOutPerPrimitive) << "\n"
   2371 					<< tcu::TestLog::EndMessage;
   2372 			}
   2373 		}
   2374 
   2375 		m_program = new glu::ShaderProgram(m_context.getRenderContext(), sources);
   2376 		m_testCtx.getLog() << *m_program;
   2377 		if (!m_program->isOk())
   2378 			throw tcu::TestError("failed to build program");
   2379 	}
   2380 }
   2381 
   2382 void GridRenderCase::deinit (void)
   2383 {
   2384 	delete m_program;
   2385 	m_program = DE_NULL;
   2386 
   2387 	if (m_texture)
   2388 	{
   2389 		m_context.getRenderContext().getFunctions().deleteTextures(1, &m_texture);
   2390 		m_texture = 0;
   2391 	}
   2392 }
   2393 
   2394 GridRenderCase::IterateResult GridRenderCase::iterate (void)
   2395 {
   2396 	std::vector<tcu::Surface>	renderedLayers	(m_numLayers);
   2397 	bool						allLayersOk		= true;
   2398 
   2399 	for (int ndx = 0; ndx < m_numLayers; ++ndx)
   2400 		renderedLayers[ndx].setSize(RENDER_SIZE, RENDER_SIZE);
   2401 
   2402 	m_testCtx.getLog() << tcu::TestLog::Message << "Rendering single point at the origin. Expecting yellow and green colored grid-like image. (High-frequency grid may appear unicolored)." << tcu::TestLog::EndMessage;
   2403 
   2404 	try
   2405 	{
   2406 		renderTo(renderedLayers);
   2407 	}
   2408 	catch (const AllowedRenderFailureException& ex)
   2409 	{
   2410 		// Got accepted failure
   2411 		m_testCtx.getLog()
   2412 			<< tcu::TestLog::Message
   2413 			<< "Could not render, reason: " << ex.what() << "\n"
   2414 			<< "Failure is allowed."
   2415 			<< tcu::TestLog::EndMessage;
   2416 
   2417 		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
   2418 		return STOP;
   2419 	}
   2420 
   2421 	for (int ndx = 0; ndx < m_numLayers; ++ndx)
   2422 		allLayersOk &= verifyResultLayer(ndx, renderedLayers[ndx]);
   2423 
   2424 	if (allLayersOk)
   2425 		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
   2426 	else
   2427 		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
   2428 	return STOP;
   2429 }
   2430 
   2431 void GridRenderCase::renderTo (std::vector<tcu::Surface>& dst)
   2432 {
   2433 	const glw::Functions&			gl					= m_context.getRenderContext().getFunctions();
   2434 	const int						positionLocation	= gl.getAttribLocation(m_program->getProgram(), "a_position");
   2435 	const glu::VertexArray			vao					(m_context.getRenderContext());
   2436 	de::MovePtr<glu::Framebuffer>	fbo;
   2437 
   2438 	if (positionLocation == -1)
   2439 		throw tcu::TestError("Attribute a_position location was -1");
   2440 
   2441 	gl.viewport(0, 0, dst.front().getWidth(), dst.front().getHeight());
   2442 	gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
   2443 	GLU_EXPECT_NO_ERROR(gl.getError(), "viewport");
   2444 
   2445 	gl.bindVertexArray(*vao);
   2446 	GLU_EXPECT_NO_ERROR(gl.getError(), "bind vao");
   2447 
   2448 	gl.useProgram(m_program->getProgram());
   2449 	GLU_EXPECT_NO_ERROR(gl.getError(), "use program");
   2450 
   2451 	gl.patchParameteri(GL_PATCH_VERTICES, 1);
   2452 	GLU_EXPECT_NO_ERROR(gl.getError(), "set patch param");
   2453 
   2454 	gl.vertexAttrib4f(positionLocation, 0.0f, 0.0f, 0.0f, 1.0f);
   2455 
   2456 	if (m_flags & FLAG_GEOMETRY_SCATTER_LAYERS)
   2457 	{
   2458 		// clear texture contents
   2459 		{
   2460 			glu::Framebuffer clearFbo(m_context.getRenderContext());
   2461 			gl.bindFramebuffer(GL_FRAMEBUFFER, *clearFbo);
   2462 
   2463 			for (int layerNdx = 0; layerNdx < m_numLayers; ++layerNdx)
   2464 			{
   2465 				gl.framebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_texture, 0, layerNdx);
   2466 				gl.clear(GL_COLOR_BUFFER_BIT);
   2467 			}
   2468 
   2469 			GLU_EXPECT_NO_ERROR(gl.getError(), "clear tex contents");
   2470 		}
   2471 
   2472 		// create and bind layered fbo
   2473 
   2474 		fbo = de::MovePtr<glu::Framebuffer>(new glu::Framebuffer(m_context.getRenderContext()));
   2475 
   2476 		gl.bindFramebuffer(GL_FRAMEBUFFER, **fbo);
   2477 		gl.framebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_texture, 0);
   2478 		GLU_EXPECT_NO_ERROR(gl.getError(), "gen fbo");
   2479 	}
   2480 	else
   2481 	{
   2482 		// clear viewport
   2483 		gl.clear(GL_COLOR_BUFFER_BIT);
   2484 	}
   2485 
   2486 	// draw
   2487 	{
   2488 		glw::GLenum glerror;
   2489 
   2490 		gl.drawArrays(GL_PATCHES, 0, 1);
   2491 
   2492 		glerror = gl.getError();
   2493 		if (glerror == GL_OUT_OF_MEMORY && (m_flags & FLAG_ALLOW_OUT_OF_MEMORY))
   2494 			throw AllowedRenderFailureException("got GL_OUT_OF_MEMORY while drawing");
   2495 
   2496 		GLU_EXPECT_NO_ERROR(glerror, "draw patches");
   2497 	}
   2498 
   2499 	// Read layers
   2500 
   2501 	if (m_flags & FLAG_GEOMETRY_SCATTER_LAYERS)
   2502 	{
   2503 		glu::Framebuffer readFbo(m_context.getRenderContext());
   2504 		gl.bindFramebuffer(GL_FRAMEBUFFER, *readFbo);
   2505 
   2506 		for (int layerNdx = 0; layerNdx < m_numLayers; ++layerNdx)
   2507 		{
   2508 			gl.framebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_texture, 0, layerNdx);
   2509 			glu::readPixels(m_context.getRenderContext(), 0, 0, dst[layerNdx].getAccess());
   2510 			GLU_EXPECT_NO_ERROR(gl.getError(), "read pixels");
   2511 		}
   2512 	}
   2513 	else
   2514 	{
   2515 		glu::readPixels(m_context.getRenderContext(), 0, 0, dst.front().getAccess());
   2516 		GLU_EXPECT_NO_ERROR(gl.getError(), "read pixels");
   2517 	}
   2518 }
   2519 
   2520 bool GridRenderCase::verifyResultLayer (int layerNdx, const tcu::Surface& image)
   2521 {
   2522 	tcu::Surface	errorMask	(image.getWidth(), image.getHeight());
   2523 	bool			foundError	= false;
   2524 
   2525 	tcu::clear(errorMask.getAccess(), tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f));
   2526 
   2527 	m_testCtx.getLog() << tcu::TestLog::Message << "Verifying output layer " << layerNdx  << tcu::TestLog::EndMessage;
   2528 
   2529 	for (int y = 0; y < image.getHeight(); ++y)
   2530 	for (int x = 0; x < image.getWidth(); ++x)
   2531 	{
   2532 		const int		threshold	= 8;
   2533 		const tcu::RGBA	color		= image.getPixel(x, y);
   2534 
   2535 		// Color must be a linear combination of green and yellow
   2536 		if (color.getGreen() < 255 - threshold || color.getBlue() > threshold)
   2537 		{
   2538 			errorMask.setPixel(x, y, tcu::RGBA::red());
   2539 			foundError = true;
   2540 		}
   2541 	}
   2542 
   2543 	if (!foundError)
   2544 	{
   2545 		m_testCtx.getLog()
   2546 			<< tcu::TestLog::Message << "Image valid." << tcu::TestLog::EndMessage
   2547 			<< tcu::TestLog::ImageSet("ImageVerification", "Image verification")
   2548 			<< tcu::TestLog::Image("Result", "Rendered result", image.getAccess())
   2549 			<< tcu::TestLog::EndImageSet;
   2550 		return true;
   2551 	}
   2552 	else
   2553 	{
   2554 		m_testCtx.getLog()
   2555 			<< tcu::TestLog::Message << "Image verification failed, found invalid pixels." << tcu::TestLog::EndMessage
   2556 			<< tcu::TestLog::ImageSet("ImageVerification", "Image verification")
   2557 			<< tcu::TestLog::Image("Result", "Rendered result", image.getAccess())
   2558 			<< tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess())
   2559 			<< tcu::TestLog::EndImageSet;
   2560 		return false;
   2561 	}
   2562 }
   2563 
   2564 std::string GridRenderCase::getVertexSource (void)
   2565 {
   2566 	return specializeShader(s_positionVertexShader, m_context.getRenderContext().getType());
   2567 }
   2568 
   2569 std::string GridRenderCase::getFragmentSource (void)
   2570 {
   2571 	const char* source = "${VERSION_DECL}\n"
   2572 						 "flat in mediump vec4 v_color;\n"
   2573 						 "layout(location = 0) out mediump vec4 fragColor;\n"
   2574 						 "void main (void)\n"
   2575 						 "{\n"
   2576 						 "	fragColor = v_color;\n"
   2577 						 "}\n";
   2578 
   2579 	return specializeShader(source, m_context.getRenderContext().getType());
   2580 }
   2581 
   2582 std::string GridRenderCase::getTessellationControlSource (int tessLevel)
   2583 {
   2584 	std::ostringstream buf;
   2585 
   2586 	buf <<	"${VERSION_DECL}\n"
   2587 			"${EXTENSION_TESSELATION_SHADER}"
   2588 			"layout(vertices=1) out;\n"
   2589 			"\n"
   2590 			"void main()\n"
   2591 			"{\n"
   2592 			"	gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
   2593 			"	gl_TessLevelOuter[0] = " << tessLevel << ".0;\n"
   2594 			"	gl_TessLevelOuter[1] = " << tessLevel << ".0;\n"
   2595 			"	gl_TessLevelOuter[2] = " << tessLevel << ".0;\n"
   2596 			"	gl_TessLevelOuter[3] = " << tessLevel << ".0;\n"
   2597 			"	gl_TessLevelInner[0] = " << tessLevel << ".0;\n"
   2598 			"	gl_TessLevelInner[1] = " << tessLevel << ".0;\n"
   2599 			"}\n";
   2600 
   2601 	return specializeShader(buf.str(), m_context.getRenderContext().getType());
   2602 }
   2603 
   2604 std::string GridRenderCase::getTessellationEvaluationSource (int tessLevel)
   2605 {
   2606 	std::ostringstream buf;
   2607 
   2608 	buf <<	"${VERSION_DECL}\n"
   2609 			"${EXTENSION_TESSELATION_SHADER}"
   2610 			"layout(quads) in;\n"
   2611 			"\n"
   2612 			"out mediump ivec2 v_tessellationGridPosition;\n"
   2613 			"\n"
   2614 			"// note: No need to use precise gl_Position since position does not depend on order\n"
   2615 			"void main (void)\n"
   2616 			"{\n";
   2617 
   2618 	if (m_flags & (FLAG_GEOMETRY_SCATTER_INSTANCES | FLAG_GEOMETRY_SCATTER_PRIMITIVES | FLAG_GEOMETRY_SCATTER_LAYERS))
   2619 		buf <<	"	// Cover only a small area in a corner. The area will be expanded in geometry shader to cover whole viewport\n"
   2620 				"	gl_Position = vec4(gl_TessCoord.x * 0.3 - 1.0, gl_TessCoord.y * 0.3 - 1.0, 0.0, 1.0);\n";
   2621 	else
   2622 		buf <<	"	// Fill the whole viewport\n"
   2623 				"	gl_Position = vec4(gl_TessCoord.x * 2.0 - 1.0, gl_TessCoord.y * 2.0 - 1.0, 0.0, 1.0);\n";
   2624 
   2625 	buf <<	"	// Calculate position in tessellation grid\n"
   2626 			"	v_tessellationGridPosition = ivec2(round(gl_TessCoord.xy * float(" << tessLevel << ")));\n"
   2627 			"}\n";
   2628 
   2629 	return specializeShader(buf.str(), m_context.getRenderContext().getType());
   2630 }
   2631 
   2632 std::string GridRenderCase::getGeometryShaderSource (int numPrimitives, int numInstances, int tessLevel)
   2633 {
   2634 	std::ostringstream buf;
   2635 
   2636 	buf	<<	"${VERSION_DECL}\n"
   2637 			"${EXTENSION_GEOMETRY_SHADER}"
   2638 			"layout(triangles, invocations=" << numInstances << ") in;\n"
   2639 			"layout(triangle_strip, max_vertices=" << ((m_flags & FLAG_GEOMETRY_SEPARATE_PRIMITIVES) ? (4 * numPrimitives) : (numPrimitives + 2)) << ") out;\n"
   2640 			"\n"
   2641 			"in mediump ivec2 v_tessellationGridPosition[];\n"
   2642 			"flat out highp vec4 v_color;\n"
   2643 			"\n"
   2644 			"void main ()\n"
   2645 			"{\n"
   2646 			"	const float equalThreshold = 0.001;\n"
   2647 			"	const float gapOffset = 0.0001; // subdivision performed by the geometry shader might produce gaps. Fill potential gaps by enlarging the output slice a little.\n"
   2648 			"\n"
   2649 			"	// Input triangle is generated from an axis-aligned rectangle by splitting it in half\n"
   2650 			"	// Original rectangle can be found by finding the bounding AABB of the triangle\n"
   2651 			"	vec4 aabb = vec4(min(gl_in[0].gl_Position.x, min(gl_in[1].gl_Position.x, gl_in[2].gl_Position.x)),\n"
   2652 			"	                 min(gl_in[0].gl_Position.y, min(gl_in[1].gl_Position.y, gl_in[2].gl_Position.y)),\n"
   2653 			"	                 max(gl_in[0].gl_Position.x, max(gl_in[1].gl_Position.x, gl_in[2].gl_Position.x)),\n"
   2654 			"	                 max(gl_in[0].gl_Position.y, max(gl_in[1].gl_Position.y, gl_in[2].gl_Position.y)));\n"
   2655 			"\n"
   2656 			"	// Location in tessellation grid\n"
   2657 			"	ivec2 gridPosition = ivec2(min(v_tessellationGridPosition[0], min(v_tessellationGridPosition[1], v_tessellationGridPosition[2])));\n"
   2658 			"\n"
   2659 			"	// Which triangle of the two that split the grid cell\n"
   2660 			"	int numVerticesOnBottomEdge = 0;\n"
   2661 			"	for (int ndx = 0; ndx < 3; ++ndx)\n"
   2662 			"		if (abs(gl_in[ndx].gl_Position.y - aabb.w) < equalThreshold)\n"
   2663 			"			++numVerticesOnBottomEdge;\n"
   2664 			"	bool isBottomTriangle = numVerticesOnBottomEdge == 2;\n"
   2665 			"\n";
   2666 
   2667 	if (m_flags & FLAG_GEOMETRY_SCATTER_PRIMITIVES)
   2668 	{
   2669 		// scatter primitives
   2670 		buf <<	"	// Draw grid cells\n"
   2671 				"	int inputTriangleNdx = gl_InvocationID * 2 + ((isBottomTriangle) ? (1) : (0));\n"
   2672 				"	for (int ndx = 0; ndx < " << numPrimitives << "; ++ndx)\n"
   2673 				"	{\n"
   2674 				"		ivec2 dstGridSize = ivec2(" << tessLevel << " * " << numPrimitives << ", 2 * " << tessLevel << " * " << numInstances << ");\n"
   2675 				"		ivec2 dstGridNdx = ivec2(" << tessLevel << " * ndx + gridPosition.x, " << tessLevel << " * inputTriangleNdx + 2 * gridPosition.y + ndx * 127) % dstGridSize;\n"
   2676 				"		vec4 dstArea;\n"
   2677 				"		dstArea.x = float(dstGridNdx.x) / float(dstGridSize.x) * 2.0 - 1.0 - gapOffset;\n"
   2678 				"		dstArea.y = float(dstGridNdx.y) / float(dstGridSize.y) * 2.0 - 1.0 - gapOffset;\n"
   2679 				"		dstArea.z = float(dstGridNdx.x+1) / float(dstGridSize.x) * 2.0 - 1.0 + gapOffset;\n"
   2680 				"		dstArea.w = float(dstGridNdx.y+1) / float(dstGridSize.y) * 2.0 - 1.0 + gapOffset;\n"
   2681 				"\n"
   2682 				"		vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n"
   2683 				"		vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n"
   2684 				"		vec4 outputColor = (((dstGridNdx.y + dstGridNdx.x) % 2) == 0) ? (green) : (yellow);\n"
   2685 				"\n"
   2686 				"		gl_Position = vec4(dstArea.x, dstArea.y, 0.0, 1.0);\n"
   2687 				"		v_color = outputColor;\n"
   2688 				"		EmitVertex();\n"
   2689 				"\n"
   2690 				"		gl_Position = vec4(dstArea.x, dstArea.w, 0.0, 1.0);\n"
   2691 				"		v_color = outputColor;\n"
   2692 				"		EmitVertex();\n"
   2693 				"\n"
   2694 				"		gl_Position = vec4(dstArea.z, dstArea.y, 0.0, 1.0);\n"
   2695 				"		v_color = outputColor;\n"
   2696 				"		EmitVertex();\n"
   2697 				"\n"
   2698 				"		gl_Position = vec4(dstArea.z, dstArea.w, 0.0, 1.0);\n"
   2699 				"		v_color = outputColor;\n"
   2700 				"		EmitVertex();\n"
   2701 				"		EndPrimitive();\n"
   2702 				"	}\n";
   2703 	}
   2704 	else if (m_flags & FLAG_GEOMETRY_SCATTER_LAYERS)
   2705 	{
   2706 		// Number of subrectangle instances = num layers
   2707 		DE_ASSERT(m_numLayers == numInstances * 2);
   2708 
   2709 		buf <<	"	// Draw grid cells, send each primitive to a separate layer\n"
   2710 				"	int baseLayer = gl_InvocationID * 2 + ((isBottomTriangle) ? (1) : (0));\n"
   2711 				"	for (int ndx = 0; ndx < " << numPrimitives << "; ++ndx)\n"
   2712 				"	{\n"
   2713 				"		ivec2 dstGridSize = ivec2(" << tessLevel << " * " << numPrimitives << ", " << tessLevel << ");\n"
   2714 				"		ivec2 dstGridNdx = ivec2((gridPosition.x * " << numPrimitives << " * 7 + ndx)*13, (gridPosition.y * 127 + ndx) * 19) % dstGridSize;\n"
   2715 				"		vec4 dstArea;\n"
   2716 				"		dstArea.x = float(dstGridNdx.x) / float(dstGridSize.x) * 2.0 - 1.0 - gapOffset;\n"
   2717 				"		dstArea.y = float(dstGridNdx.y) / float(dstGridSize.y) * 2.0 - 1.0 - gapOffset;\n"
   2718 				"		dstArea.z = float(dstGridNdx.x+1) / float(dstGridSize.x) * 2.0 - 1.0 + gapOffset;\n"
   2719 				"		dstArea.w = float(dstGridNdx.y+1) / float(dstGridSize.y) * 2.0 - 1.0 + gapOffset;\n"
   2720 				"\n"
   2721 				"		vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n"
   2722 				"		vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n"
   2723 				"		vec4 outputColor = (((dstGridNdx.y + dstGridNdx.x) % 2) == 0) ? (green) : (yellow);\n"
   2724 				"\n"
   2725 				"		gl_Position = vec4(dstArea.x, dstArea.y, 0.0, 1.0);\n"
   2726 				"		v_color = outputColor;\n"
   2727 				"		gl_Layer = ((baseLayer + ndx) * 11) % " << m_numLayers << ";\n"
   2728 				"		EmitVertex();\n"
   2729 				"\n"
   2730 				"		gl_Position = vec4(dstArea.x, dstArea.w, 0.0, 1.0);\n"
   2731 				"		v_color = outputColor;\n"
   2732 				"		gl_Layer = ((baseLayer + ndx) * 11) % " << m_numLayers << ";\n"
   2733 				"		EmitVertex();\n"
   2734 				"\n"
   2735 				"		gl_Position = vec4(dstArea.z, dstArea.y, 0.0, 1.0);\n"
   2736 				"		v_color = outputColor;\n"
   2737 				"		gl_Layer = ((baseLayer + ndx) * 11) % " << m_numLayers << ";\n"
   2738 				"		EmitVertex();\n"
   2739 				"\n"
   2740 				"		gl_Position = vec4(dstArea.z, dstArea.w, 0.0, 1.0);\n"
   2741 				"		v_color = outputColor;\n"
   2742 				"		gl_Layer = ((baseLayer + ndx) * 11) % " << m_numLayers << ";\n"
   2743 				"		EmitVertex();\n"
   2744 				"		EndPrimitive();\n"
   2745 				"	}\n";
   2746 	}
   2747 	else
   2748 	{
   2749 		if (m_flags & FLAG_GEOMETRY_SCATTER_INSTANCES)
   2750 		{
   2751 			buf <<	"	// Scatter slices\n"
   2752 					"	int inputTriangleNdx = gl_InvocationID * 2 + ((isBottomTriangle) ? (1) : (0));\n"
   2753 					"	ivec2 srcSliceNdx = ivec2(gridPosition.x, gridPosition.y * " << (numInstances*2) << " + inputTriangleNdx);\n"
   2754 					"	ivec2 dstSliceNdx = ivec2(7 * srcSliceNdx.x, 127 * srcSliceNdx.y) % ivec2(" << tessLevel << ", " << tessLevel << " * " << (numInstances*2) << ");\n"
   2755 					"\n"
   2756 					"	// Draw slice to the dstSlice slot\n"
   2757 					"	vec4 outputSliceArea;\n"
   2758 					"	outputSliceArea.x = float(dstSliceNdx.x) / float(" << tessLevel << ") * 2.0 - 1.0 - gapOffset;\n"
   2759 					"	outputSliceArea.y = float(dstSliceNdx.y) / float(" << (tessLevel * numInstances * 2) << ") * 2.0 - 1.0 - gapOffset;\n"
   2760 					"	outputSliceArea.z = float(dstSliceNdx.x+1) / float(" << tessLevel << ") * 2.0 - 1.0 + gapOffset;\n"
   2761 					"	outputSliceArea.w = float(dstSliceNdx.y+1) / float(" << (tessLevel * numInstances * 2) << ") * 2.0 - 1.0 + gapOffset;\n";
   2762 		}
   2763 		else
   2764 		{
   2765 			buf <<	"	// Fill the input area with slices\n"
   2766 					"	// Upper triangle produces slices only to the upper half of the quad and vice-versa\n"
   2767 					"	float triangleOffset = (isBottomTriangle) ? ((aabb.w + aabb.y) / 2.0) : (aabb.y);\n"
   2768 					"	// Each slice is a invocation\n"
   2769 					"	float sliceHeight = (aabb.w - aabb.y) / float(2 * " << numInstances << ");\n"
   2770 					"	float invocationOffset = float(gl_InvocationID) * sliceHeight;\n"
   2771 					"\n"
   2772 					"	vec4 outputSliceArea;\n"
   2773 					"	outputSliceArea.x = aabb.x - gapOffset;\n"
   2774 					"	outputSliceArea.y = triangleOffset + invocationOffset - gapOffset;\n"
   2775 					"	outputSliceArea.z = aabb.z + gapOffset;\n"
   2776 					"	outputSliceArea.w = triangleOffset + invocationOffset + sliceHeight + gapOffset;\n";
   2777 		}
   2778 
   2779 		buf <<	"\n"
   2780 				"	// Draw slice\n"
   2781 				"	for (int ndx = 0; ndx < " << ((numPrimitives+2)/2) << "; ++ndx)\n"
   2782 				"	{\n"
   2783 				"		vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n"
   2784 				"		vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n"
   2785 				"		vec4 outputColor = (((gl_InvocationID + ndx) % 2) == 0) ? (green) : (yellow);\n"
   2786 				"		float xpos = mix(outputSliceArea.x, outputSliceArea.z, float(ndx) / float(" << (numPrimitives/2) << "));\n"
   2787 				"\n"
   2788 				"		gl_Position = vec4(xpos, outputSliceArea.y, 0.0, 1.0);\n"
   2789 				"		v_color = outputColor;\n"
   2790 				"		EmitVertex();\n"
   2791 				"\n"
   2792 				"		gl_Position = vec4(xpos, outputSliceArea.w, 0.0, 1.0);\n"
   2793 				"		v_color = outputColor;\n"
   2794 				"		EmitVertex();\n"
   2795 				"	}\n";
   2796 	}
   2797 
   2798 	buf <<	"}\n";
   2799 
   2800 	return specializeShader(buf.str(), m_context.getRenderContext().getType());
   2801 }
   2802 
   2803 class FeedbackRecordVariableSelectionCase : public TestCase
   2804 {
   2805 public:
   2806 						FeedbackRecordVariableSelectionCase		(Context& context, const char* name, const char* description);
   2807 						~FeedbackRecordVariableSelectionCase	(void);
   2808 
   2809 private:
   2810 	void				init									(void);
   2811 	void				deinit									(void);
   2812 	IterateResult		iterate									(void);
   2813 
   2814 	std::string			getVertexSource							(void);
   2815 	std::string			getFragmentSource						(void);
   2816 	std::string			getTessellationControlSource			(void);
   2817 	std::string			getTessellationEvaluationSource			(void);
   2818 	std::string			getGeometrySource						(void);
   2819 
   2820 	glu::ShaderProgram*	m_program;
   2821 	deUint32			m_xfbBuf;
   2822 };
   2823 
   2824 FeedbackRecordVariableSelectionCase::FeedbackRecordVariableSelectionCase (Context& context, const char* name, const char* description)
   2825 	: TestCase	(context, name, description)
   2826 	, m_program	(DE_NULL)
   2827 	, m_xfbBuf	(0)
   2828 {
   2829 }
   2830 
   2831 FeedbackRecordVariableSelectionCase::~FeedbackRecordVariableSelectionCase (void)
   2832 {
   2833 	deinit();
   2834 }
   2835 
   2836 void FeedbackRecordVariableSelectionCase::init (void)
   2837 {
   2838 	const bool supportsES32 = glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2));
   2839 
   2840 	if (!supportsES32 &&
   2841 		(!m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader") ||
   2842 		 !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader")))
   2843 		throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader and GL_EXT_geometry_shader extensions");
   2844 
   2845 	m_testCtx.getLog() << tcu::TestLog::Message << "Declaring multiple output variables with the same name in multiple shader stages. Capturing the value of the varying using transform feedback." << tcu::TestLog::EndMessage;
   2846 
   2847 	// gen feedback buffer fit for 1 triangle (4 components)
   2848 	{
   2849 		static const tcu::Vec4 initialData[3] =
   2850 		{
   2851 			tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.0f),
   2852 			tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.0f),
   2853 			tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.0f),
   2854 		};
   2855 
   2856 		const glw::Functions& gl = m_context.getRenderContext().getFunctions();
   2857 
   2858 		m_testCtx.getLog() << tcu::TestLog::Message << "Creating buffer for transform feedback. Allocating storage for one triangle. Filling with -1.0" << tcu::TestLog::EndMessage;
   2859 
   2860 		gl.genBuffers(1, &m_xfbBuf);
   2861 		gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_xfbBuf);
   2862 		gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, (int)(sizeof(tcu::Vec4[3])), initialData, GL_DYNAMIC_READ);
   2863 		GLU_EXPECT_NO_ERROR(gl.getError(), "gen xfb buf");
   2864 	}
   2865 
   2866 	// gen shader
   2867 	m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
   2868 																	 << glu::VertexSource(getVertexSource())
   2869 																	 << glu::FragmentSource(getFragmentSource())
   2870 																	 << glu::TessellationControlSource(getTessellationControlSource())
   2871 																	 << glu::TessellationEvaluationSource(getTessellationEvaluationSource())
   2872 																	 << glu::GeometrySource(getGeometrySource())
   2873 																	 << glu::TransformFeedbackMode(GL_INTERLEAVED_ATTRIBS)
   2874 																	 << glu::TransformFeedbackVarying("tf_feedback"));
   2875 	m_testCtx.getLog() << *m_program;
   2876 
   2877 	if (!m_program->isOk())
   2878 		throw tcu::TestError("could not build program");
   2879 }
   2880 
   2881 void FeedbackRecordVariableSelectionCase::deinit (void)
   2882 {
   2883 	delete m_program;
   2884 	m_program = DE_NULL;
   2885 
   2886 	if (m_xfbBuf)
   2887 	{
   2888 		m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_xfbBuf);
   2889 		m_xfbBuf = 0;
   2890 	}
   2891 }
   2892 
   2893 FeedbackRecordVariableSelectionCase::IterateResult FeedbackRecordVariableSelectionCase::iterate (void)
   2894 {
   2895 	const glw::Functions&	gl		= m_context.getRenderContext().getFunctions();
   2896 	const int				posLoc	= gl.getAttribLocation(m_program->getProgram(), "a_position");
   2897 	const glu::VertexArray	vao		(m_context.getRenderContext());
   2898 
   2899 	if (posLoc == -1)
   2900 		throw tcu::TestError("a_position attribute location was -1");
   2901 
   2902 	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
   2903 
   2904 	m_testCtx.getLog() << tcu::TestLog::Message << "Rendering a patch of size 3." << tcu::TestLog::EndMessage;
   2905 
   2906 	// Render and feed back
   2907 
   2908 	gl.viewport(0, 0, 1, 1);
   2909 	gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
   2910 	gl.clear(GL_COLOR_BUFFER_BIT);
   2911 	GLU_EXPECT_NO_ERROR(gl.getError(), "clear");
   2912 
   2913 	gl.bindVertexArray(*vao);
   2914 	GLU_EXPECT_NO_ERROR(gl.getError(), "bindVertexArray");
   2915 
   2916 	gl.vertexAttrib4f(posLoc, 0.0f, 0.0f, 0.0f, 1.0f);
   2917 	GLU_EXPECT_NO_ERROR(gl.getError(), "vertexAttrib4f");
   2918 
   2919 	gl.useProgram(m_program->getProgram());
   2920 	GLU_EXPECT_NO_ERROR(gl.getError(), "use program");
   2921 
   2922 	gl.patchParameteri(GL_PATCH_VERTICES, 3);
   2923 	GLU_EXPECT_NO_ERROR(gl.getError(), "set patch param");
   2924 
   2925 	gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_xfbBuf);
   2926 	GLU_EXPECT_NO_ERROR(gl.getError(), "bind xfb buf");
   2927 
   2928 	gl.beginTransformFeedback(GL_TRIANGLES);
   2929 	GLU_EXPECT_NO_ERROR(gl.getError(), "beginTransformFeedback");
   2930 
   2931 	gl.drawArrays(GL_PATCHES, 0, 3);
   2932 	GLU_EXPECT_NO_ERROR(gl.getError(), "drawArrays");
   2933 
   2934 	gl.endTransformFeedback();
   2935 	GLU_EXPECT_NO_ERROR(gl.getError(), "beginTransformFeedback");
   2936 
   2937 	m_testCtx.getLog() << tcu::TestLog::Message << "Verifying the value of tf_feedback using transform feedback, expecting (3.0, 3.0, 3.0, 3.0)." << tcu::TestLog::EndMessage;
   2938 
   2939 	// Read back result (one triangle)
   2940 	{
   2941 		tcu::Vec4	feedbackValues[3];
   2942 		const void* mapPtr				= gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, (int)sizeof(feedbackValues), GL_MAP_READ_BIT);
   2943 		GLU_EXPECT_NO_ERROR(gl.getError(), "mapBufferRange");
   2944 
   2945 		if (mapPtr == DE_NULL)
   2946 			throw tcu::TestError("mapBufferRange returned null");
   2947 
   2948 		deMemcpy(feedbackValues, mapPtr, sizeof(feedbackValues));
   2949 
   2950 		if (gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER) != GL_TRUE)
   2951 			throw tcu::TestError("unmapBuffer did not return TRUE");
   2952 
   2953 		for (int ndx = 0; ndx < 3; ++ndx)
   2954 		{
   2955 			if (!tcu::boolAll(tcu::lessThan(tcu::abs(feedbackValues[ndx] - tcu::Vec4(3.0f)), tcu::Vec4(0.001f))))
   2956 			{
   2957 				m_testCtx.getLog() << tcu::TestLog::Message << "Feedback vertex " << ndx << ": expected (3.0, 3.0, 3.0, 3.0), got " << feedbackValues[ndx] << tcu::TestLog::EndMessage;
   2958 				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got unexpected feedback results");
   2959 			}
   2960 		}
   2961 	}
   2962 
   2963 	return STOP;
   2964 }
   2965 
   2966 std::string FeedbackRecordVariableSelectionCase::getVertexSource (void)
   2967 {
   2968 	std::string source =	"${VERSION_DECL}\n"
   2969 							"in highp vec4 a_position;\n"
   2970 							"out highp vec4 tf_feedback;\n"
   2971 							"void main()\n"
   2972 							"{\n"
   2973 							"	gl_Position = a_position;\n"
   2974 							"	tf_feedback = vec4(1.0, 1.0, 1.0, 1.0);\n"
   2975 							"}\n";
   2976 
   2977 	return specializeShader(source, m_context.getRenderContext().getType());
   2978 }
   2979 
   2980 std::string FeedbackRecordVariableSelectionCase::getFragmentSource (void)
   2981 {
   2982 	return specializeShader(s_whiteOutputFragmentShader, m_context.getRenderContext().getType());
   2983 }
   2984 
   2985 std::string FeedbackRecordVariableSelectionCase::getTessellationControlSource (void)
   2986 {
   2987 	std::string source =	"${VERSION_DECL}\n"
   2988 							"${EXTENSION_TESSELATION_SHADER}"
   2989 							"layout(vertices=3) out;\n"
   2990 							"void main()\n"
   2991 							"{\n"
   2992 							"	gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
   2993 							"	gl_TessLevelOuter[0] = 1.0;\n"
   2994 							"	gl_TessLevelOuter[1] = 1.0;\n"
   2995 							"	gl_TessLevelOuter[2] = 1.0;\n"
   2996 							"	gl_TessLevelInner[0] = 1.0;\n"
   2997 							"}\n";
   2998 
   2999 	return specializeShader(source, m_context.getRenderContext().getType());
   3000 }
   3001 
   3002 std::string FeedbackRecordVariableSelectionCase::getTessellationEvaluationSource (void)
   3003 {
   3004 	std::string source =	"${VERSION_DECL}\n"
   3005 							"${EXTENSION_TESSELATION_SHADER}"
   3006 							"layout(triangles) in;\n"
   3007 							"out highp vec4 tf_feedback;\n"
   3008 							"void main()\n"
   3009 							"{\n"
   3010 							"	gl_Position = gl_in[0].gl_Position * gl_TessCoord.x + gl_in[1].gl_Position * gl_TessCoord.y + gl_in[2].gl_Position * gl_TessCoord.z;\n"
   3011 							"	tf_feedback = vec4(2.0, 2.0, 2.0, 2.0);\n"
   3012 							"}\n";
   3013 
   3014 	return specializeShader(source, m_context.getRenderContext().getType());
   3015 }
   3016 
   3017 std::string FeedbackRecordVariableSelectionCase::getGeometrySource(void)
   3018 {
   3019 	std::string source =	"${VERSION_DECL}\n"
   3020 							"${EXTENSION_GEOMETRY_SHADER}"
   3021 							"layout (triangles) in;\n"
   3022 							"layout (triangle_strip, max_vertices=3) out;\n"
   3023 							"out highp vec4 tf_feedback;\n"
   3024 							"void main()\n"
   3025 							"{\n"
   3026 							"	for (int ndx = 0; ndx < 3; ++ndx)\n"
   3027 							"	{\n"
   3028 							"		gl_Position = gl_in[ndx].gl_Position + vec4(float(ndx), float(ndx)*float(ndx), 0.0, 0.0);\n"
   3029 							"		tf_feedback = vec4(3.0, 3.0, 3.0, 3.0);\n"
   3030 							"		EmitVertex();\n"
   3031 							"	}\n"
   3032 							"	EndPrimitive();\n"
   3033 							"}\n";
   3034 
   3035 	return specializeShader(source, m_context.getRenderContext().getType());
   3036 }
   3037 
   3038 } // anonymous
   3039 
   3040 TessellationGeometryInteractionTests::TessellationGeometryInteractionTests (Context& context)
   3041 	: TestCaseGroup(context, "tessellation_geometry_interaction", "Tessellation and geometry shader interaction tests")
   3042 {
   3043 }
   3044 
   3045 TessellationGeometryInteractionTests::~TessellationGeometryInteractionTests (void)
   3046 {
   3047 }
   3048 
   3049 void TessellationGeometryInteractionTests::init (void)
   3050 {
   3051 	tcu::TestCaseGroup* const renderGroup		= new tcu::TestCaseGroup(m_testCtx, "render",		"Various render tests");
   3052 	tcu::TestCaseGroup* const feedbackGroup		= new tcu::TestCaseGroup(m_testCtx, "feedback",		"Test transform feedback");
   3053 	tcu::TestCaseGroup* const pointSizeGroup	= new tcu::TestCaseGroup(m_testCtx, "point_size",	"Test point size");
   3054 
   3055 	addChild(renderGroup);
   3056 	addChild(feedbackGroup);
   3057 	addChild(pointSizeGroup);
   3058 
   3059 	// .render
   3060 	{
   3061 		tcu::TestCaseGroup* const passthroughGroup	= new tcu::TestCaseGroup(m_testCtx, "passthrough",	"Render various types with either passthrough geometry or tessellation shader");
   3062 		tcu::TestCaseGroup* const limitGroup		= new tcu::TestCaseGroup(m_testCtx, "limits",		"Render with properties near their limits");
   3063 		tcu::TestCaseGroup* const scatterGroup		= new tcu::TestCaseGroup(m_testCtx, "scatter",		"Scatter output primitives");
   3064 
   3065 		renderGroup->addChild(passthroughGroup);
   3066 		renderGroup->addChild(limitGroup);
   3067 		renderGroup->addChild(scatterGroup);
   3068 
   3069 		// .passthrough
   3070 		{
   3071 			// tessellate_tris_passthrough_geometry_no_change
   3072 			// tessellate_quads_passthrough_geometry_no_change
   3073 			// tessellate_isolines_passthrough_geometry_no_change
   3074 			passthroughGroup->addChild(new IdentityGeometryShaderCase(m_context, "tessellate_tris_passthrough_geometry_no_change",		"Passthrough geometry shader has no effect", IdentityGeometryShaderCase::CASE_TRIANGLES));
   3075 			passthroughGroup->addChild(new IdentityGeometryShaderCase(m_context, "tessellate_quads_passthrough_geometry_no_change",		"Passthrough geometry shader has no effect", IdentityGeometryShaderCase::CASE_QUADS));
   3076 			passthroughGroup->addChild(new IdentityGeometryShaderCase(m_context, "tessellate_isolines_passthrough_geometry_no_change",	"Passthrough geometry shader has no effect", IdentityGeometryShaderCase::CASE_ISOLINES));
   3077 
   3078 			// passthrough_tessellation_geometry_shade_triangles_no_change
   3079 			// passthrough_tessellation_geometry_shade_lines_no_change
   3080 			passthroughGroup->addChild(new IdentityTessellationShaderCase(m_context, "passthrough_tessellation_geometry_shade_triangles_no_change",	"Passthrough tessellation shader has no effect", IdentityTessellationShaderCase::CASE_TRIANGLES));
   3081 			passthroughGroup->addChild(new IdentityTessellationShaderCase(m_context, "passthrough_tessellation_geometry_shade_lines_no_change",		"Passthrough tessellation shader has no effect", IdentityTessellationShaderCase::CASE_ISOLINES));
   3082 		}
   3083 
   3084 		// .limits
   3085 		{
   3086 			static const struct LimitCaseDef
   3087 			{
   3088 				const char*	name;
   3089 				const char*	desc;
   3090 				int			flags;
   3091 			} cases[] =
   3092 			{
   3093 				// Test single limit
   3094 				{
   3095 					"output_required_max_tessellation",
   3096 					"Minimum maximum tessellation level",
   3097 					GridRenderCase::FLAG_TESSELLATION_MAX_SPEC
   3098 				},
   3099 				{
   3100 					"output_implementation_max_tessellation",
   3101 					"Maximum tessellation level supported by the implementation",
   3102 					GridRenderCase::FLAG_TESSELLATION_MAX_IMPLEMENTATION
   3103 				},
   3104 				{
   3105 					"output_required_max_geometry",
   3106 					"Output minimum maximum number of vertices the geometry shader",
   3107 					GridRenderCase::FLAG_GEOMETRY_MAX_SPEC
   3108 				},
   3109 				{
   3110 					"output_implementation_max_geometry",
   3111 					"Output maximum number of vertices in the geometry shader supported by the implementation",
   3112 					GridRenderCase::FLAG_GEOMETRY_MAX_IMPLEMENTATION
   3113 				},
   3114 				{
   3115 					"output_required_max_invocations",
   3116 					"Minimum maximum number of geometry shader invocations",
   3117 					GridRenderCase::FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC
   3118 				},
   3119 				{
   3120 					"output_implementation_max_invocations",
   3121 					"Maximum number of geometry shader invocations supported by the implementation",
   3122 					GridRenderCase::FLAG_GEOMETRY_INVOCATIONS_MAX_IMPLEMENTATION
   3123 				},
   3124 			};
   3125 
   3126 			for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(cases); ++ndx)
   3127 				limitGroup->addChild(new GridRenderCase(m_context, cases[ndx].name, cases[ndx].desc, cases[ndx].flags));
   3128 		}
   3129 
   3130 		// .scatter
   3131 		{
   3132 			scatterGroup->addChild(new GridRenderCase(m_context,
   3133 													  "geometry_scatter_instances",
   3134 													  "Each geometry shader instance outputs its primitives far from other instances of the same execution",
   3135 													  GridRenderCase::FLAG_GEOMETRY_SCATTER_INSTANCES));
   3136 			scatterGroup->addChild(new GridRenderCase(m_context,
   3137 													  "geometry_scatter_primitives",
   3138 													  "Each geometry shader instance outputs its primitives far from other primitives of the same instance",
   3139 													  GridRenderCase::FLAG_GEOMETRY_SCATTER_PRIMITIVES | GridRenderCase::FLAG_GEOMETRY_SEPARATE_PRIMITIVES));
   3140 			scatterGroup->addChild(new GridRenderCase(m_context,
   3141 													  "geometry_scatter_layers",
   3142 													  "Each geometry shader instance outputs its primitives to multiple layers and far from other primitives of the same instance",
   3143 													  GridRenderCase::FLAG_GEOMETRY_SCATTER_LAYERS | GridRenderCase::FLAG_GEOMETRY_SEPARATE_PRIMITIVES));
   3144 		}
   3145 	}
   3146 
   3147 	// .feedback
   3148 	{
   3149 		static const struct PrimitiveCaseConfig
   3150 		{
   3151 			const char*											name;
   3152 			const char*											description;
   3153 			FeedbackPrimitiveTypeCase::TessellationOutputType	tessellationOutput;
   3154 			FeedbackPrimitiveTypeCase::TessellationPointMode	tessellationPointMode;
   3155 			FeedbackPrimitiveTypeCase::GeometryOutputType		geometryOutputType;
   3156 		} caseConfigs[] =
   3157 		{
   3158 			// tess output triangles -> geo input triangles, output points
   3159 			{
   3160 				"tessellation_output_triangles_geometry_output_points",
   3161 				"Tessellation outputs triangles, geometry outputs points",
   3162 				FeedbackPrimitiveTypeCase::TESSELLATION_OUT_TRIANGLES,
   3163 				FeedbackPrimitiveTypeCase::TESSELLATION_POINTMODE_OFF,
   3164 				FeedbackPrimitiveTypeCase::GEOMETRY_OUTPUT_POINTS
   3165 			},
   3166 
   3167 			// tess output quads <-> geo input triangles, output points
   3168 			{
   3169 				"tessellation_output_quads_geometry_output_points",
   3170 				"Tessellation outputs quads, geometry outputs points",
   3171 				FeedbackPrimitiveTypeCase::TESSELLATION_OUT_QUADS,
   3172 				FeedbackPrimitiveTypeCase::TESSELLATION_POINTMODE_OFF,
   3173 				FeedbackPrimitiveTypeCase::GEOMETRY_OUTPUT_POINTS
   3174 			},
   3175 
   3176 			// tess output isolines <-> geo input lines, output points
   3177 			{
   3178 				"tessellation_output_isolines_geometry_output_points",
   3179 				"Tessellation outputs isolines, geometry outputs points",
   3180 				FeedbackPrimitiveTypeCase::TESSELLATION_OUT_ISOLINES,
   3181 				FeedbackPrimitiveTypeCase::TESSELLATION_POINTMODE_OFF,
   3182 				FeedbackPrimitiveTypeCase::GEOMETRY_OUTPUT_POINTS
   3183 			},
   3184 
   3185 			// tess output triangles, point_mode <-> geo input points, output lines
   3186 			{
   3187 				"tessellation_output_triangles_point_mode_geometry_output_lines",
   3188 				"Tessellation outputs triangles in point mode, geometry outputs lines",
   3189 				FeedbackPrimitiveTypeCase::TESSELLATION_OUT_TRIANGLES,
   3190 				FeedbackPrimitiveTypeCase::TESSELLATION_POINTMODE_ON,
   3191 				FeedbackPrimitiveTypeCase::GEOMETRY_OUTPUT_LINES
   3192 			},
   3193 
   3194 			// tess output quads, point_mode <-> geo input points, output lines
   3195 			{
   3196 				"tessellation_output_quads_point_mode_geometry_output_lines",
   3197 				"Tessellation outputs quads in point mode, geometry outputs lines",
   3198 				FeedbackPrimitiveTypeCase::TESSELLATION_OUT_QUADS,
   3199 				FeedbackPrimitiveTypeCase::TESSELLATION_POINTMODE_ON,
   3200 				FeedbackPrimitiveTypeCase::GEOMETRY_OUTPUT_LINES
   3201 			},
   3202 
   3203 			// tess output isolines, point_mode <-> geo input points, output triangles
   3204 			{
   3205 				"tessellation_output_isolines_point_mode_geometry_output_triangles",
   3206 				"Tessellation outputs isolines in point mode, geometry outputs triangles",
   3207 				FeedbackPrimitiveTypeCase::TESSELLATION_OUT_ISOLINES,
   3208 				FeedbackPrimitiveTypeCase::TESSELLATION_POINTMODE_ON,
   3209 				FeedbackPrimitiveTypeCase::GEOMETRY_OUTPUT_TRIANGLES
   3210 			},
   3211 		};
   3212 
   3213 		for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(caseConfigs); ++ndx)
   3214 		{
   3215 			feedbackGroup->addChild(new FeedbackPrimitiveTypeCase(m_context,
   3216 																  caseConfigs[ndx].name,
   3217 																  caseConfigs[ndx].description,
   3218 																  caseConfigs[ndx].tessellationOutput,
   3219 																  caseConfigs[ndx].tessellationPointMode,
   3220 																  caseConfigs[ndx].geometryOutputType));
   3221 		}
   3222 
   3223 		feedbackGroup->addChild(new FeedbackRecordVariableSelectionCase(m_context, "record_variable_selection", "Record a variable that has been declared as an output variable in multiple shader stages"));
   3224 	}
   3225 
   3226 	// .point_size
   3227 	{
   3228 		static const int caseFlags[] =
   3229 		{
   3230 			PointSizeCase::FLAG_VERTEX_SET,
   3231 												PointSizeCase::FLAG_TESSELLATION_EVALUATION_SET,
   3232 																										PointSizeCase::FLAG_GEOMETRY_SET,
   3233 			PointSizeCase::FLAG_VERTEX_SET	|	PointSizeCase::FLAG_TESSELLATION_CONTROL_SET,
   3234 			PointSizeCase::FLAG_VERTEX_SET	|	PointSizeCase::FLAG_TESSELLATION_EVALUATION_SET,
   3235 			PointSizeCase::FLAG_VERTEX_SET	|	PointSizeCase::FLAG_TESSELLATION_DONT_SET,
   3236 			PointSizeCase::FLAG_VERTEX_SET	|															PointSizeCase::FLAG_GEOMETRY_SET,
   3237 			PointSizeCase::FLAG_VERTEX_SET	|	PointSizeCase::FLAG_TESSELLATION_EVALUATION_SET		|	PointSizeCase::FLAG_GEOMETRY_SET,
   3238 			PointSizeCase::FLAG_VERTEX_SET	|	PointSizeCase::FLAG_TESSELLATION_ADD				|	PointSizeCase::FLAG_GEOMETRY_ADD,
   3239 			PointSizeCase::FLAG_VERTEX_SET	|	PointSizeCase::FLAG_TESSELLATION_EVALUATION_SET		|	PointSizeCase::FLAG_GEOMETRY_DONT_SET,
   3240 		};
   3241 
   3242 		for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(caseFlags); ++ndx)
   3243 		{
   3244 			const std::string name = PointSizeCase::genTestCaseName(caseFlags[ndx]);
   3245 			const std::string desc = PointSizeCase::genTestCaseDescription(caseFlags[ndx]);
   3246 
   3247 			pointSizeGroup->addChild(new PointSizeCase(m_context, name.c_str(), desc.c_str(), caseFlags[ndx]));
   3248 		}
   3249 	}
   3250 }
   3251 
   3252 } // Functional
   3253 } // gles31
   3254 } // deqp
   3255