Home | History | Annotate | Download | only in stress
      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 stress tests.
     22  *//*--------------------------------------------------------------------*/
     23 
     24 #include "es31sTessellationGeometryInteractionTests.hpp"
     25 
     26 #include "tcuTestLog.hpp"
     27 #include "tcuRenderTarget.hpp"
     28 #include "tcuSurface.hpp"
     29 #include "tcuTextureUtil.hpp"
     30 #include "gluRenderContext.hpp"
     31 #include "gluShaderProgram.hpp"
     32 #include "gluContextInfo.hpp"
     33 #include "gluObjectWrapper.hpp"
     34 #include "gluPixelTransfer.hpp"
     35 #include "glwFunctions.hpp"
     36 #include "glwEnums.hpp"
     37 #include "deStringUtil.hpp"
     38 #include "deUniquePtr.hpp"
     39 
     40 #include <sstream>
     41 
     42 namespace deqp
     43 {
     44 namespace gles31
     45 {
     46 namespace Stress
     47 {
     48 namespace
     49 {
     50 
     51 class AllowedRenderFailureException : public std::runtime_error
     52 {
     53 public:
     54 	AllowedRenderFailureException (const char* message) : std::runtime_error(message) { }
     55 };
     56 
     57 class GridRenderCase : public TestCase
     58 {
     59 public:
     60 	enum Flags
     61 	{
     62 		FLAG_TESSELLATION_MAX_SPEC						= 0x0001,
     63 		FLAG_TESSELLATION_MAX_IMPLEMENTATION			= 0x0002,
     64 		FLAG_GEOMETRY_MAX_SPEC							= 0x0004,
     65 		FLAG_GEOMETRY_MAX_IMPLEMENTATION				= 0x0008,
     66 		FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC				= 0x0010,
     67 		FLAG_GEOMETRY_INVOCATIONS_MAX_IMPLEMENTATION	= 0x0020,
     68 	};
     69 
     70 						GridRenderCase					(Context& context, const char* name, const char* description, int flags);
     71 						~GridRenderCase					(void);
     72 
     73 private:
     74 	void				init							(void);
     75 	void				deinit							(void);
     76 	IterateResult		iterate							(void);
     77 
     78 	void				renderTo						(std::vector<tcu::Surface>& dst);
     79 	bool				verifyResultLayer				(int layerNdx, const tcu::Surface& dst);
     80 
     81 	const char*			getVertexSource					(void);
     82 	const char*			getFragmentSource				(void);
     83 	std::string			getTessellationControlSource	(int tessLevel);
     84 	std::string			getTessellationEvaluationSource	(int tessLevel);
     85 	std::string			getGeometryShaderSource			(int numPrimitives, int numInstances);
     86 
     87 	enum
     88 	{
     89 		RENDER_SIZE = 256
     90 	};
     91 
     92 	const int			m_flags;
     93 
     94 	glu::ShaderProgram*	m_program;
     95 	int					m_numLayers;
     96 };
     97 
     98 GridRenderCase::GridRenderCase (Context& context, const char* name, const char* description, int flags)
     99 	: TestCase		(context, name, description)
    100 	, m_flags		(flags)
    101 	, m_program		(DE_NULL)
    102 	, m_numLayers	(1)
    103 {
    104 	DE_ASSERT(((m_flags & FLAG_TESSELLATION_MAX_SPEC) == 0)			|| ((m_flags & FLAG_TESSELLATION_MAX_IMPLEMENTATION) == 0));
    105 	DE_ASSERT(((m_flags & FLAG_GEOMETRY_MAX_SPEC) == 0)				|| ((m_flags & FLAG_GEOMETRY_MAX_IMPLEMENTATION) == 0));
    106 	DE_ASSERT(((m_flags & FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC) == 0)	|| ((m_flags & FLAG_GEOMETRY_INVOCATIONS_MAX_IMPLEMENTATION) == 0));
    107 }
    108 
    109 GridRenderCase::~GridRenderCase (void)
    110 {
    111 	deinit();
    112 }
    113 
    114 void GridRenderCase::init (void)
    115 {
    116 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
    117 
    118 	// Requirements
    119 
    120 	if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader") ||
    121 		!m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader"))
    122 		throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader and GL_EXT_geometry_shader extensions");
    123 
    124 	if (m_context.getRenderTarget().getWidth() < RENDER_SIZE ||
    125 		m_context.getRenderTarget().getHeight() < RENDER_SIZE)
    126 		throw tcu::NotSupportedError("Test requires " + de::toString<int>(RENDER_SIZE) + "x" + de::toString<int>(RENDER_SIZE) + " or larger render target.");
    127 
    128 	// Log
    129 
    130 	m_testCtx.getLog()
    131 		<< tcu::TestLog::Message
    132 		<< "Testing tessellation and geometry shaders that output a large number of primitives.\n"
    133 		<< getDescription()
    134 		<< tcu::TestLog::EndMessage;
    135 
    136 	// Gen program
    137 	{
    138 		glu::ProgramSources	sources;
    139 		int					tessGenLevel = -1;
    140 
    141 		sources	<< glu::VertexSource(getVertexSource())
    142 				<< glu::FragmentSource(getFragmentSource());
    143 
    144 		// Tessellation limits
    145 		{
    146 			if (m_flags & FLAG_TESSELLATION_MAX_IMPLEMENTATION)
    147 			{
    148 				gl.getIntegerv(GL_MAX_TESS_GEN_LEVEL, &tessGenLevel);
    149 				GLU_EXPECT_NO_ERROR(gl.getError(), "query tessellation limits");
    150 			}
    151 			else if (m_flags & FLAG_TESSELLATION_MAX_SPEC)
    152 			{
    153 				tessGenLevel = 64;
    154 			}
    155 			else
    156 			{
    157 				tessGenLevel = 5;
    158 			}
    159 
    160 			m_testCtx.getLog()
    161 					<< tcu::TestLog::Message
    162 					<< "Tessellation level: " << tessGenLevel << ", mode = quad.\n"
    163 					<< "\tEach input patch produces " << (tessGenLevel*tessGenLevel) << " (" << (tessGenLevel*tessGenLevel*2) << " triangles)\n"
    164 					<< tcu::TestLog::EndMessage;
    165 
    166 			sources << glu::TessellationControlSource(getTessellationControlSource(tessGenLevel))
    167 					<< glu::TessellationEvaluationSource(getTessellationEvaluationSource(tessGenLevel));
    168 		}
    169 
    170 		// Geometry limits
    171 		{
    172 			int		geometryOutputComponents		= -1;
    173 			int		geometryOutputVertices			= -1;
    174 			int		geometryTotalOutputComponents	= -1;
    175 			int		geometryShaderInvocations		= -1;
    176 			bool	logGeometryLimits				= false;
    177 			bool	logInvocationLimits				= false;
    178 
    179 			if (m_flags & FLAG_GEOMETRY_MAX_IMPLEMENTATION)
    180 			{
    181 				m_testCtx.getLog() << tcu::TestLog::Message << "Using implementation maximum geometry shader output limits." << tcu::TestLog::EndMessage;
    182 
    183 				gl.getIntegerv(GL_MAX_GEOMETRY_OUTPUT_COMPONENTS, &geometryOutputComponents);
    184 				gl.getIntegerv(GL_MAX_GEOMETRY_OUTPUT_VERTICES, &geometryOutputVertices);
    185 				gl.getIntegerv(GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS, &geometryTotalOutputComponents);
    186 				GLU_EXPECT_NO_ERROR(gl.getError(), "query geometry limits");
    187 
    188 				logGeometryLimits = true;
    189 			}
    190 			else if (m_flags & FLAG_GEOMETRY_MAX_SPEC)
    191 			{
    192 				m_testCtx.getLog() << tcu::TestLog::Message << "Using geometry shader extension minimum maximum output limits." << tcu::TestLog::EndMessage;
    193 
    194 				geometryOutputComponents = 128;
    195 				geometryOutputVertices = 256;
    196 				geometryTotalOutputComponents = 1024;
    197 				logGeometryLimits = true;
    198 			}
    199 			else
    200 			{
    201 				geometryOutputComponents = 128;
    202 				geometryOutputVertices = 16;
    203 				geometryTotalOutputComponents = 1024;
    204 			}
    205 
    206 			if (m_flags & FLAG_GEOMETRY_INVOCATIONS_MAX_IMPLEMENTATION)
    207 			{
    208 				gl.getIntegerv(GL_MAX_GEOMETRY_SHADER_INVOCATIONS, &geometryShaderInvocations);
    209 				GLU_EXPECT_NO_ERROR(gl.getError(), "query geometry invocation limits");
    210 
    211 				logInvocationLimits = true;
    212 			}
    213 			else if (m_flags & FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC)
    214 			{
    215 				geometryShaderInvocations = 32;
    216 				logInvocationLimits = true;
    217 			}
    218 			else
    219 			{
    220 				geometryShaderInvocations = 4;
    221 			}
    222 
    223 			if (logGeometryLimits || logInvocationLimits)
    224 			{
    225 				tcu::MessageBuilder msg(&m_testCtx.getLog());
    226 
    227 				msg << "Geometry shader, targeting following limits:\n";
    228 
    229 				if (logGeometryLimits)
    230 					msg	<< "\tGL_MAX_GEOMETRY_OUTPUT_COMPONENTS = " << geometryOutputComponents << "\n"
    231 						<< "\tGL_MAX_GEOMETRY_OUTPUT_VERTICES = " << geometryOutputVertices << "\n"
    232 						<< "\tGL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS = " << geometryTotalOutputComponents << "\n";
    233 
    234 				if (logInvocationLimits)
    235 					msg << "\tGL_MAX_GEOMETRY_SHADER_INVOCATIONS = " << geometryShaderInvocations;
    236 
    237 				msg << tcu::TestLog::EndMessage;
    238 			}
    239 
    240 
    241 			{
    242 				const int	numComponentsPerVertex		= 8; // vec4 pos, vec4 color
    243 
    244 				// If FLAG_GEOMETRY_SEPARATE_PRIMITIVES is not set, geometry shader fills a rectangle area in slices.
    245 				// Each slice is a triangle strip and is generated by a single shader invocation.
    246 				// One slice with 4 segment ends (nodes) and 3 segments:
    247 				//    .__.__.__.
    248 				//    |\ |\ |\ |
    249 				//    |_\|_\|_\|
    250 
    251 				const int	numSliceNodesComponentLimit			= geometryTotalOutputComponents / (2 * numComponentsPerVertex);			// each node 2 vertices
    252 				const int	numSliceNodesOutputLimit			= geometryOutputVertices / 2;											// each node 2 vertices
    253 				const int	numSliceNodes						= de::min(numSliceNodesComponentLimit, numSliceNodesOutputLimit);
    254 
    255 				const int	numVerticesPerInvocation			= numSliceNodes * 2;
    256 				const int	numPrimitivesPerInvocation			= (numSliceNodes - 1) * 2;
    257 
    258 				const int	geometryVerticesPerPrimitive		= numVerticesPerInvocation * geometryShaderInvocations;
    259 				const int	geometryPrimitivesOutPerPrimitive	= numPrimitivesPerInvocation * geometryShaderInvocations;
    260 
    261 				m_testCtx.getLog()
    262 					<< tcu::TestLog::Message
    263 					<< "Geometry shader:\n"
    264 					<< "\tTotal output vertex count per invocation: " << (numVerticesPerInvocation) << "\n"
    265 					<< "\tTotal output primitive count per invocation: " << (numPrimitivesPerInvocation) << "\n"
    266 					<< "\tNumber of invocations per primitive: " << geometryShaderInvocations << "\n"
    267 					<< "\tTotal output vertex count per input primitive: " << (geometryVerticesPerPrimitive) << "\n"
    268 					<< "\tTotal output primitive count per input primitive: " << (geometryPrimitivesOutPerPrimitive) << "\n"
    269 					<< tcu::TestLog::EndMessage;
    270 
    271 				sources	<< glu::GeometrySource(getGeometryShaderSource(numPrimitivesPerInvocation, geometryShaderInvocations));
    272 
    273 				m_testCtx.getLog()
    274 					<< tcu::TestLog::Message
    275 					<< "Program:\n"
    276 					<< "\tTotal program output vertices count per input patch: " << (tessGenLevel*tessGenLevel*2 * geometryVerticesPerPrimitive) << "\n"
    277 					<< "\tTotal program output primitive count per input patch: " << (tessGenLevel*tessGenLevel*2 * geometryPrimitivesOutPerPrimitive) << "\n"
    278 					<< tcu::TestLog::EndMessage;
    279 			}
    280 		}
    281 
    282 		m_program = new glu::ShaderProgram(m_context.getRenderContext(), sources);
    283 		m_testCtx.getLog() << *m_program;
    284 		if (!m_program->isOk())
    285 			throw tcu::TestError("failed to build program");
    286 	}
    287 }
    288 
    289 void GridRenderCase::deinit (void)
    290 {
    291 	delete m_program;
    292 	m_program = DE_NULL;
    293 }
    294 
    295 GridRenderCase::IterateResult GridRenderCase::iterate (void)
    296 {
    297 	std::vector<tcu::Surface>	renderedLayers	(m_numLayers);
    298 	bool						allLayersOk		= true;
    299 
    300 	for (int ndx = 0; ndx < m_numLayers; ++ndx)
    301 		renderedLayers[ndx].setSize(RENDER_SIZE, RENDER_SIZE);
    302 
    303 	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;
    304 
    305 	try
    306 	{
    307 		renderTo(renderedLayers);
    308 	}
    309 	catch (const AllowedRenderFailureException& ex)
    310 	{
    311 		// Got accepted failure
    312 		m_testCtx.getLog()
    313 			<< tcu::TestLog::Message
    314 			<< "Could not render, reason: " << ex.what() << "\n"
    315 			<< "Failure is allowed."
    316 			<< tcu::TestLog::EndMessage;
    317 
    318 		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
    319 		return STOP;
    320 	}
    321 
    322 	for (int ndx = 0; ndx < m_numLayers; ++ndx)
    323 		allLayersOk &= verifyResultLayer(ndx, renderedLayers[ndx]);
    324 
    325 	if (allLayersOk)
    326 		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
    327 	else
    328 		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
    329 	return STOP;
    330 }
    331 
    332 void GridRenderCase::renderTo (std::vector<tcu::Surface>& dst)
    333 {
    334 	const glw::Functions&			gl					= m_context.getRenderContext().getFunctions();
    335 	const int						positionLocation	= gl.getAttribLocation(m_program->getProgram(), "a_position");
    336 	const glu::VertexArray			vao					(m_context.getRenderContext());
    337 
    338 	if (positionLocation == -1)
    339 		throw tcu::TestError("Attribute a_position location was -1");
    340 
    341 	gl.viewport(0, 0, dst.front().getWidth(), dst.front().getHeight());
    342 	gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
    343 	GLU_EXPECT_NO_ERROR(gl.getError(), "viewport");
    344 
    345 	gl.bindVertexArray(*vao);
    346 	GLU_EXPECT_NO_ERROR(gl.getError(), "bind vao");
    347 
    348 	gl.useProgram(m_program->getProgram());
    349 	GLU_EXPECT_NO_ERROR(gl.getError(), "use program");
    350 
    351 	gl.patchParameteri(GL_PATCH_VERTICES, 1);
    352 	GLU_EXPECT_NO_ERROR(gl.getError(), "set patch param");
    353 
    354 	gl.vertexAttrib4f(positionLocation, 0.0f, 0.0f, 0.0f, 1.0f);
    355 
    356 	// clear viewport
    357 	gl.clear(GL_COLOR_BUFFER_BIT);
    358 
    359 	// draw
    360 	{
    361 		glw::GLenum glerror;
    362 
    363 		gl.drawArrays(GL_PATCHES, 0, 1);
    364 
    365 		// allow always OOM
    366 		glerror = gl.getError();
    367 		if (glerror == GL_OUT_OF_MEMORY)
    368 			throw AllowedRenderFailureException("got GL_OUT_OF_MEMORY while drawing");
    369 
    370 		GLU_EXPECT_NO_ERROR(glerror, "draw patches");
    371 	}
    372 
    373 	// Read layers
    374 
    375 	glu::readPixels(m_context.getRenderContext(), 0, 0, dst.front().getAccess());
    376 	GLU_EXPECT_NO_ERROR(gl.getError(), "read pixels");
    377 }
    378 
    379 bool GridRenderCase::verifyResultLayer (int layerNdx, const tcu::Surface& image)
    380 {
    381 	tcu::Surface	errorMask	(image.getWidth(), image.getHeight());
    382 	bool			foundError	= false;
    383 
    384 	tcu::clear(errorMask.getAccess(), tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f));
    385 
    386 	m_testCtx.getLog() << tcu::TestLog::Message << "Verifying output layer " << layerNdx  << tcu::TestLog::EndMessage;
    387 
    388 	for (int y = 0; y < image.getHeight(); ++y)
    389 	for (int x = 0; x < image.getWidth(); ++x)
    390 	{
    391 		const int		threshold	= 8;
    392 		const tcu::RGBA	color		= image.getPixel(x, y);
    393 
    394 		// Color must be a linear combination of green and yellow
    395 		if (color.getGreen() < 255 - threshold || color.getBlue() > threshold)
    396 		{
    397 			errorMask.setPixel(x, y, tcu::RGBA::red());
    398 			foundError = true;
    399 		}
    400 	}
    401 
    402 	if (!foundError)
    403 	{
    404 		m_testCtx.getLog()
    405 			<< tcu::TestLog::Message << "Image valid." << tcu::TestLog::EndMessage
    406 			<< tcu::TestLog::ImageSet("ImageVerification", "Image verification")
    407 			<< tcu::TestLog::Image("Result", "Rendered result", image.getAccess())
    408 			<< tcu::TestLog::EndImageSet;
    409 		return true;
    410 	}
    411 	else
    412 	{
    413 		m_testCtx.getLog()
    414 			<< tcu::TestLog::Message << "Image verification failed, found invalid pixels." << tcu::TestLog::EndMessage
    415 			<< tcu::TestLog::ImageSet("ImageVerification", "Image verification")
    416 			<< tcu::TestLog::Image("Result", "Rendered result", image.getAccess())
    417 			<< tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess())
    418 			<< tcu::TestLog::EndImageSet;
    419 		return false;
    420 	}
    421 }
    422 
    423 const char* GridRenderCase::getVertexSource (void)
    424 {
    425 	return	"#version 310 es\n"
    426 			"in highp vec4 a_position;\n"
    427 			"void main (void)\n"
    428 			"{\n"
    429 			"	gl_Position = a_position;\n"
    430 			"}\n";
    431 }
    432 
    433 const char* GridRenderCase::getFragmentSource (void)
    434 {
    435 	return	"#version 310 es\n"
    436 			"flat in mediump vec4 v_color;\n"
    437 			"layout(location = 0) out mediump vec4 fragColor;\n"
    438 			"void main (void)\n"
    439 			"{\n"
    440 			"	fragColor = v_color;\n"
    441 			"}\n";
    442 }
    443 
    444 std::string GridRenderCase::getTessellationControlSource (int tessLevel)
    445 {
    446 	std::ostringstream buf;
    447 
    448 	buf <<	"#version 310 es\n"
    449 			"#extension GL_EXT_tessellation_shader : require\n"
    450 			"layout(vertices=1) out;\n"
    451 			"\n"
    452 			"void main()\n"
    453 			"{\n"
    454 			"	gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
    455 			"	gl_TessLevelOuter[0] = " << tessLevel << ".0;\n"
    456 			"	gl_TessLevelOuter[1] = " << tessLevel << ".0;\n"
    457 			"	gl_TessLevelOuter[2] = " << tessLevel << ".0;\n"
    458 			"	gl_TessLevelOuter[3] = " << tessLevel << ".0;\n"
    459 			"	gl_TessLevelInner[0] = " << tessLevel << ".0;\n"
    460 			"	gl_TessLevelInner[1] = " << tessLevel << ".0;\n"
    461 			"}\n";
    462 
    463 	return buf.str();
    464 }
    465 
    466 std::string GridRenderCase::getTessellationEvaluationSource (int tessLevel)
    467 {
    468 	std::ostringstream buf;
    469 
    470 	buf <<	"#version 310 es\n"
    471 			"#extension GL_EXT_tessellation_shader : require\n"
    472 			"layout(quads) in;\n"
    473 			"\n"
    474 			"out mediump ivec2 v_tessellationGridPosition;\n"
    475 			"\n"
    476 			"// note: No need to use precise gl_Position since position does not depend on order\n"
    477 			"void main (void)\n"
    478 			"{\n"
    479 			"	// Fill the whole viewport\n"
    480 			"	gl_Position = vec4(gl_TessCoord.x * 2.0 - 1.0, gl_TessCoord.y * 2.0 - 1.0, 0.0, 1.0);\n"
    481 			"	// Calculate position in tessellation grid\n"
    482 			"	v_tessellationGridPosition = ivec2(round(gl_TessCoord.xy * float(" << tessLevel << ")));\n"
    483 			"}\n";
    484 
    485 	return buf.str();
    486 }
    487 
    488 std::string GridRenderCase::getGeometryShaderSource (int numPrimitives, int numInstances)
    489 {
    490 	std::ostringstream buf;
    491 
    492 	buf	<<	"#version 310 es\n"
    493 			"#extension GL_EXT_geometry_shader : require\n"
    494 			"layout(triangles, invocations=" << numInstances << ") in;\n"
    495 			"layout(triangle_strip, max_vertices=" << (numPrimitives + 2) << ") out;\n"
    496 			"\n"
    497 			"in mediump ivec2 v_tessellationGridPosition[];\n"
    498 			"flat out highp vec4 v_color;\n"
    499 			"\n"
    500 			"void main ()\n"
    501 			"{\n"
    502 			"	const float equalThreshold = 0.001;\n"
    503 			"	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"
    504 			"\n"
    505 			"	// Input triangle is generated from an axis-aligned rectangle by splitting it in half\n"
    506 			"	// Original rectangle can be found by finding the bounding AABB of the triangle\n"
    507 			"	vec4 aabb = vec4(min(gl_in[0].gl_Position.x, min(gl_in[1].gl_Position.x, gl_in[2].gl_Position.x)),\n"
    508 			"	                 min(gl_in[0].gl_Position.y, min(gl_in[1].gl_Position.y, gl_in[2].gl_Position.y)),\n"
    509 			"	                 max(gl_in[0].gl_Position.x, max(gl_in[1].gl_Position.x, gl_in[2].gl_Position.x)),\n"
    510 			"	                 max(gl_in[0].gl_Position.y, max(gl_in[1].gl_Position.y, gl_in[2].gl_Position.y)));\n"
    511 			"\n"
    512 			"	// Location in tessellation grid\n"
    513 			"	ivec2 gridPosition = ivec2(min(v_tessellationGridPosition[0], min(v_tessellationGridPosition[1], v_tessellationGridPosition[2])));\n"
    514 			"\n"
    515 			"	// Which triangle of the two that split the grid cell\n"
    516 			"	int numVerticesOnBottomEdge = 0;\n"
    517 			"	for (int ndx = 0; ndx < 3; ++ndx)\n"
    518 			"		if (abs(gl_in[ndx].gl_Position.y - aabb.w) < equalThreshold)\n"
    519 			"			++numVerticesOnBottomEdge;\n"
    520 			"	bool isBottomTriangle = numVerticesOnBottomEdge == 2;\n"
    521 			"\n"
    522 			"	// Fill the input area with slices\n"
    523 			"	// Upper triangle produces slices only to the upper half of the quad and vice-versa\n"
    524 			"	float triangleOffset = (isBottomTriangle) ? ((aabb.w + aabb.y) / 2.0) : (aabb.y);\n"
    525 			"	// Each slice is a invocation\n"
    526 			"	float sliceHeight = (aabb.w - aabb.y) / float(2 * " << numInstances << ");\n"
    527 			"	float invocationOffset = float(gl_InvocationID) * sliceHeight;\n"
    528 			"\n"
    529 			"	vec4 outputSliceArea;\n"
    530 			"	outputSliceArea.x = aabb.x - gapOffset;\n"
    531 			"	outputSliceArea.y = triangleOffset + invocationOffset - gapOffset;\n"
    532 			"	outputSliceArea.z = aabb.z + gapOffset;\n"
    533 			"	outputSliceArea.w = triangleOffset + invocationOffset + sliceHeight + gapOffset;\n""\n"
    534 			"	// Draw slice\n"
    535 			"	for (int ndx = 0; ndx < " << ((numPrimitives+2)/2) << "; ++ndx)\n"
    536 			"	{\n"
    537 			"		vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n"
    538 			"		vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n"
    539 			"		vec4 outputColor = (((gl_InvocationID + ndx) % 2) == 0) ? (green) : (yellow);\n"
    540 			"		float xpos = mix(outputSliceArea.x, outputSliceArea.z, float(ndx) / float(" << (numPrimitives/2) << "));\n"
    541 			"\n"
    542 			"		gl_Position = vec4(xpos, outputSliceArea.y, 0.0, 1.0);\n"
    543 			"		v_color = outputColor;\n"
    544 			"		EmitVertex();\n"
    545 			"\n"
    546 			"		gl_Position = vec4(xpos, outputSliceArea.w, 0.0, 1.0);\n"
    547 			"		v_color = outputColor;\n"
    548 			"		EmitVertex();\n"
    549 			"	}\n"
    550 			"}\n";
    551 
    552 	return buf.str();
    553 }
    554 
    555 } // anonymous
    556 
    557 TessellationGeometryInteractionTests::TessellationGeometryInteractionTests (Context& context)
    558 	: TestCaseGroup(context, "tessellation_geometry_interaction", "Tessellation and geometry shader interaction stress tests")
    559 {
    560 }
    561 
    562 TessellationGeometryInteractionTests::~TessellationGeometryInteractionTests (void)
    563 {
    564 }
    565 
    566 void TessellationGeometryInteractionTests::init (void)
    567 {
    568 	tcu::TestCaseGroup* const multilimitGroup = new tcu::TestCaseGroup(m_testCtx, "render_multiple_limits", "Various render tests");
    569 
    570 	addChild(multilimitGroup);
    571 
    572 	// .render_multiple_limits
    573 	{
    574 		static const struct LimitCaseDef
    575 		{
    576 			const char*	name;
    577 			const char*	desc;
    578 			int			flags;
    579 		} cases[] =
    580 		{
    581 			// Test multiple limits at the same time
    582 
    583 			{
    584 				"output_required_max_tessellation_max_geometry",
    585 				"Minimum maximum tessellation level and geometry shader output vertices",
    586 				GridRenderCase::FLAG_TESSELLATION_MAX_SPEC | GridRenderCase::FLAG_GEOMETRY_MAX_SPEC
    587 			},
    588 			{
    589 				"output_implementation_max_tessellation_max_geometry",
    590 				"Maximum tessellation level and geometry shader output vertices supported by the implementation",
    591 				GridRenderCase::FLAG_TESSELLATION_MAX_IMPLEMENTATION | GridRenderCase::FLAG_GEOMETRY_MAX_IMPLEMENTATION
    592 			},
    593 			{
    594 				"output_required_max_tessellation_max_invocations",
    595 				"Minimum maximum tessellation level and geometry shader invocations",
    596 				GridRenderCase::FLAG_TESSELLATION_MAX_SPEC | GridRenderCase::FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC
    597 			},
    598 			{
    599 				"output_implementation_max_tessellation_max_invocations",
    600 				"Maximum tessellation level and geometry shader invocations supported by the implementation",
    601 				GridRenderCase::FLAG_TESSELLATION_MAX_IMPLEMENTATION | GridRenderCase::FLAG_GEOMETRY_INVOCATIONS_MAX_IMPLEMENTATION
    602 			},
    603 			{
    604 				"output_required_max_geometry_max_invocations",
    605 				"Minimum maximum geometry shader output vertices and invocations",
    606 				GridRenderCase::FLAG_GEOMETRY_MAX_SPEC | GridRenderCase::FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC
    607 			},
    608 			{
    609 				"output_implementation_max_geometry_max_invocations",
    610 				"Maximum geometry shader output vertices and invocations invocations supported by the implementation",
    611 				GridRenderCase::FLAG_GEOMETRY_MAX_IMPLEMENTATION | GridRenderCase::FLAG_GEOMETRY_INVOCATIONS_MAX_IMPLEMENTATION
    612 			},
    613 
    614 			// Test all limits simultaneously
    615 			{
    616 				"output_max_required",
    617 				"Output minimum maximum number of vertices",
    618 				GridRenderCase::FLAG_TESSELLATION_MAX_SPEC | GridRenderCase::FLAG_GEOMETRY_MAX_SPEC | GridRenderCase::FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC
    619 			},
    620 			{
    621 				"output_max_implementation",
    622 				"Output maximum number of vertices supported by the implementation",
    623 				GridRenderCase::FLAG_TESSELLATION_MAX_IMPLEMENTATION | GridRenderCase::FLAG_GEOMETRY_MAX_IMPLEMENTATION | GridRenderCase::FLAG_GEOMETRY_INVOCATIONS_MAX_IMPLEMENTATION
    624 			},
    625 		};
    626 
    627 		for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(cases); ++ndx)
    628 			multilimitGroup->addChild(new GridRenderCase(m_context, cases[ndx].name, cases[ndx].desc, cases[ndx].flags));
    629 	}
    630 }
    631 
    632 } // Stress
    633 } // gles31
    634 } // deqp
    635