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