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