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