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