• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program OpenGL ES 3.1 Module
3  * -------------------------------------------------
4  *
5  * Copyright 2014 The Android Open Source Project
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief Tessellation and geometry shader interaction 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 
48 namespace deqp
49 {
50 namespace gles31
51 {
52 namespace Functional
53 {
54 namespace
55 {
56 
specializeShader(const std::string & shaderSource,const glu::ContextType & contextType)57 static 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 
69 static 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";
75 static 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 
isBlack(const tcu::RGBA & c)82 static bool isBlack (const tcu::RGBA& c)
83 {
84 	return c.getRed() == 0 && c.getGreen() == 0 && c.getBlue() == 0;
85 }
86 
87 class IdentityShaderCase : public TestCase
88 {
89 public:
90 					IdentityShaderCase	(Context& context, const char* name, const char* description);
91 
92 protected:
93 	std::string		getVertexSource		(void) const;
94 	std::string		getFragmentSource	(void) const;
95 };
96 
IdentityShaderCase(Context & context,const char * name,const char * description)97 IdentityShaderCase::IdentityShaderCase (Context& context, const char* name, const char* description)
98 	: TestCase(context, name, description)
99 {
100 }
101 
getVertexSource(void) const102 std::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 
getFragmentSource(void) const116 std::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 
126 return specializeShader(source, m_context.getRenderContext().getType());
127 }
128 
129 class IdentityGeometryShaderCase : public IdentityShaderCase
130 {
131 public:
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 
142 private:
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 
IdentityGeometryShaderCase(Context & context,const char * name,const char * description,CaseType caseType)160 IdentityGeometryShaderCase::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 
~IdentityGeometryShaderCase(void)167 IdentityGeometryShaderCase::~IdentityGeometryShaderCase (void)
168 {
169 	deinit();
170 }
171 
init(void)172 void 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 
deinit(void)215 void 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 
iterate(void)224 IdentityGeometryShaderCase::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 
getTessellationControlSource(void) const328 std::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 
getTessellationEvaluationSource(bool geometryActive) const370 std::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 
getGeometrySource(void) const408 std::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 
436 class IdentityTessellationShaderCase : public IdentityShaderCase
437 {
438 public:
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 
448 private:
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 
IdentityTessellationShaderCase(Context & context,const char * name,const char * description,CaseType caseType)466 IdentityTessellationShaderCase::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 
~IdentityTessellationShaderCase(void)473 IdentityTessellationShaderCase::~IdentityTessellationShaderCase (void)
474 {
475 	deinit();
476 }
477 
init(void)478 void 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 
deinit(void)519 void 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 
iterate(void)528 IdentityTessellationShaderCase::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 
getTessellationControlSource(void) const644 std::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 
getTessellationEvaluationSource(void) const677 std::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 
getGeometrySource(bool tessellationActive) const708 std::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 
763 class FeedbackPrimitiveTypeCase : public TestCase
764 {
765 public:
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 
798 private:
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 
FeedbackPrimitiveTypeCase(Context & context,const char * name,const char * description,TessellationOutputType tessellationOutput,TessellationPointMode tessellationPointMode,GeometryOutputType geometryOutputType)843 FeedbackPrimitiveTypeCase::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 
~FeedbackPrimitiveTypeCase(void)864 FeedbackPrimitiveTypeCase::~FeedbackPrimitiveTypeCase (void)
865 {
866 	deinit();
867 }
868 
init(void)869 void 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 
deinit(void)950 void 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 
iterate(void)983 FeedbackPrimitiveTypeCase::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 
renderWithFeedback(tcu::Surface & dst)1026 void 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 
renderWithoutFeedback(tcu::Surface & dst)1123 void 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 
verifyFeedbackResults(const std::vector<tcu::Vec4> & feedbackResult)1160 void 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 
feedbackResultCompare(const tcu::Vec4 & a,const tcu::Vec4 & b)1231 static 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 
verifyRenderedImage(const tcu::Surface & image,const std::vector<tcu::Vec4> & tfVertices)1241 void 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 
genTransformFeedback(void)1294 void 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 
getTriangleNumOutputPrimitives(int tessellationLevel)1315 static 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 
getTriangleNumOutputPrimitivesPoints(int tessellationLevel)1325 static 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 
getNumGeneratedElementsPerPrimitive(void) const1335 int 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 
getNumGeneratedPrimitives(void) const1350 int FeedbackPrimitiveTypeCase::getNumGeneratedPrimitives (void) const
1351 {
1352 	return getNumTessellatedPrimitives() * getGeometryAmplification();
1353 }
1354 
getNumTessellatedPrimitives(void) const1355 int 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 
getGeometryAmplification(void) const1382 int 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 
getOutputPrimitiveGLType(void) const1390 glw::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 
getVertexSource(void) const1405 std::string FeedbackPrimitiveTypeCase::getVertexSource (void) const
1406 {
1407 	return specializeShader(s_positionVertexShader, m_context.getRenderContext().getType());
1408 }
1409 
getFragmentSource(void) const1410 std::string FeedbackPrimitiveTypeCase::getFragmentSource (void) const
1411 {
1412 	return specializeShader(s_whiteOutputFragmentShader, m_context.getRenderContext().getType());
1413 }
1414 
getTessellationControlSource(void) const1415 std::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 
getTessellationEvaluationSource(void) const1464 std::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 
getGeometrySource(void) const1506 std::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 
getTessellationOutputDescription(TessellationOutputType tessellationOutput,TessellationPointMode pointMode)1581 const 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 
getGeometryInputDescription(TessellationOutputType tessellationOutput,TessellationPointMode pointMode)1594 const 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 
getGeometryOutputDescription(GeometryOutputType geometryOutput)1607 const 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 
1620 class PointSizeCase : public TestCase
1621 {
1622 public:
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 
1641 private:
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 
PointSizeCase(Context & context,const char * name,const char * description,int flags)1668 PointSizeCase::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 
~PointSizeCase(void)1675 PointSizeCase::~PointSizeCase (void)
1676 {
1677 	deinit();
1678 }
1679 
genTestCaseName(int flags)1680 std::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 
genTestCaseDescription(int flags)1697 std::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 
init(void)1714 void 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 
deinit(void)1760 void PointSizeCase::deinit (void)
1761 {
1762 	delete m_program;
1763 	m_program = DE_NULL;
1764 }
1765 
iterate(void)1766 PointSizeCase::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 
checkExtensions(void) const1780 void 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 
checkPointSizeRequirements(void) const1817 void 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 
renderTo(tcu::Surface & dst)1829 void 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 
verifyImage(const tcu::Surface & src)1871 bool 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 
getExpectedPointSize(void) const1953 int 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 
genVertexSource(void) const1985 std::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 
genFragmentSource(void) const2003 std::string PointSizeCase::genFragmentSource (void) const
2004 {
2005 	return specializeShader(s_whiteOutputFragmentShader, m_context.getRenderContext().getType());
2006 }
2007 
genTessellationControlSource(void) const2008 std::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 
genTessellationEvaluationSource(void) const2036 std::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 
genGeometrySource(void) const2066 std::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 
2094 class AllowedRenderFailureException : public std::runtime_error
2095 {
2096 public:
AllowedRenderFailureException(const char * message)2097 	AllowedRenderFailureException (const char* message) : std::runtime_error(message) { }
2098 };
2099 
2100 class GridRenderCase : public TestCase
2101 {
2102 public:
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 
2123 private:
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 
GridRenderCase(Context & context,const char * name,const char * description,int flags)2149 GridRenderCase::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 
~GridRenderCase(void)2162 GridRenderCase::~GridRenderCase (void)
2163 {
2164 	deinit();
2165 }
2166 
init(void)2167 void 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 
deinit(void)2382 void 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 
iterate(void)2394 GridRenderCase::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 
renderTo(std::vector<tcu::Surface> & dst)2431 void 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 
verifyResultLayer(int layerNdx,const tcu::Surface & image)2520 bool 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 
getVertexSource(void)2564 std::string GridRenderCase::getVertexSource (void)
2565 {
2566 	return specializeShader(s_positionVertexShader, m_context.getRenderContext().getType());
2567 }
2568 
getFragmentSource(void)2569 std::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 
getTessellationControlSource(int tessLevel)2582 std::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 
getTessellationEvaluationSource(int tessLevel)2604 std::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 
getGeometryShaderSource(int numPrimitives,int numInstances,int tessLevel)2632 std::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 
2803 class FeedbackRecordVariableSelectionCase : public TestCase
2804 {
2805 public:
2806 						FeedbackRecordVariableSelectionCase		(Context& context, const char* name, const char* description);
2807 						~FeedbackRecordVariableSelectionCase	(void);
2808 
2809 private:
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 
FeedbackRecordVariableSelectionCase(Context & context,const char * name,const char * description)2824 FeedbackRecordVariableSelectionCase::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 
~FeedbackRecordVariableSelectionCase(void)2831 FeedbackRecordVariableSelectionCase::~FeedbackRecordVariableSelectionCase (void)
2832 {
2833 	deinit();
2834 }
2835 
init(void)2836 void 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 
deinit(void)2881 void 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 
iterate(void)2893 FeedbackRecordVariableSelectionCase::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 
getVertexSource(void)2966 std::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 
getFragmentSource(void)2980 std::string FeedbackRecordVariableSelectionCase::getFragmentSource (void)
2981 {
2982 	return specializeShader(s_whiteOutputFragmentShader, m_context.getRenderContext().getType());
2983 }
2984 
getTessellationControlSource(void)2985 std::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 
getTessellationEvaluationSource(void)3002 std::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 
getGeometrySource(void)3017 std::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 
TessellationGeometryInteractionTests(Context & context)3040 TessellationGeometryInteractionTests::TessellationGeometryInteractionTests (Context& context)
3041 	: TestCaseGroup(context, "tessellation_geometry_interaction", "Tessellation and geometry shader interaction tests")
3042 {
3043 }
3044 
~TessellationGeometryInteractionTests(void)3045 TessellationGeometryInteractionTests::~TessellationGeometryInteractionTests (void)
3046 {
3047 }
3048 
init(void)3049 void 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