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