• 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 	const int			m_flags;
2166 
2167 	glu::ShaderProgram*	m_program;
2168 	deUint32			m_texture;
2169 	int					m_numLayers;
2170 };
2171 
GridRenderCase(Context & context,const char * name,const char * description,int flags)2172 GridRenderCase::GridRenderCase (Context& context, const char* name, const char* description, int flags)
2173 	: TestCase		(context, name, description)
2174 	, m_flags		(flags)
2175 	, m_program		(DE_NULL)
2176 	, m_texture		(0)
2177 	, m_numLayers	(1)
2178 {
2179 	DE_ASSERT(((m_flags & FLAG_TESSELLATION_MAX_SPEC) == 0)			|| ((m_flags & FLAG_TESSELLATION_MAX_IMPLEMENTATION) == 0));
2180 	DE_ASSERT(((m_flags & FLAG_GEOMETRY_MAX_SPEC) == 0)				|| ((m_flags & FLAG_GEOMETRY_MAX_IMPLEMENTATION) == 0));
2181 	DE_ASSERT(((m_flags & FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC) == 0)	|| ((m_flags & FLAG_GEOMETRY_INVOCATIONS_MAX_IMPLEMENTATION) == 0));
2182 	DE_ASSERT(((m_flags & (FLAG_GEOMETRY_SCATTER_PRIMITIVES | FLAG_GEOMETRY_SCATTER_LAYERS)) != 0) == ((m_flags & FLAG_GEOMETRY_SEPARATE_PRIMITIVES) != 0));
2183 }
2184 
~GridRenderCase(void)2185 GridRenderCase::~GridRenderCase (void)
2186 {
2187 	deinit();
2188 }
2189 
init(void)2190 void GridRenderCase::init (void)
2191 {
2192 	const glw::Functions&	gl					= m_context.getRenderContext().getFunctions();
2193 	glu::ContextType		contextType			= m_context.getRenderContext().getType();
2194 	const bool				supportsES32orGL45	= glu::contextSupports(contextType, glu::ApiType::es(3, 2)) ||
2195 												  glu::contextSupports(contextType, glu::ApiType::core(4, 5));
2196 
2197 	// Requirements
2198 
2199 	if (!supportsES32orGL45 &&
2200 		(!m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader") ||
2201 		 !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader")))
2202 		throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader and GL_EXT_geometry_shader extensions");
2203 
2204 	if ((m_flags & FLAG_GEOMETRY_SCATTER_LAYERS) == 0)
2205 	{
2206 		if (m_context.getRenderTarget().getWidth() < RENDER_SIZE ||
2207 			m_context.getRenderTarget().getHeight() < RENDER_SIZE)
2208 			throw tcu::NotSupportedError("Test requires " + de::toString<int>(RENDER_SIZE) + "x" + de::toString<int>(RENDER_SIZE) + " or larger render target.");
2209 	}
2210 
2211 	// Log
2212 
2213 	m_testCtx.getLog()
2214 		<< tcu::TestLog::Message
2215 		<< "Testing tessellation and geometry shaders that output a large number of primitives.\n"
2216 		<< getDescription()
2217 		<< tcu::TestLog::EndMessage;
2218 
2219 	// Render target
2220 	if (m_flags & FLAG_GEOMETRY_SCATTER_LAYERS)
2221 	{
2222 		// set limits
2223 		m_numLayers = 8;
2224 
2225 		m_testCtx.getLog() << tcu::TestLog::Message << "Rendering to 2d texture array, numLayers = " << m_numLayers << tcu::TestLog::EndMessage;
2226 
2227 		gl.genTextures(1, &m_texture);
2228 		gl.bindTexture(GL_TEXTURE_2D_ARRAY, m_texture);
2229 		gl.texStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_RGBA8, RENDER_SIZE, RENDER_SIZE, m_numLayers);
2230 
2231 		gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2232 		gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2233 		gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2234 		gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2235 
2236 		GLU_EXPECT_NO_ERROR(gl.getError(), "gen texture");
2237 	}
2238 
2239 	// Gen program
2240 	{
2241 		glu::ProgramSources	sources;
2242 		int					tessGenLevel = -1;
2243 
2244 		sources	<< glu::VertexSource(getVertexSource())
2245 				<< glu::FragmentSource(getFragmentSource());
2246 
2247 		// Tessellation limits
2248 		{
2249 			if (m_flags & FLAG_TESSELLATION_MAX_IMPLEMENTATION)
2250 			{
2251 				gl.getIntegerv(GL_MAX_TESS_GEN_LEVEL, &tessGenLevel);
2252 				GLU_EXPECT_NO_ERROR(gl.getError(), "query tessellation limits");
2253 			}
2254 			else if (m_flags & FLAG_TESSELLATION_MAX_SPEC)
2255 			{
2256 				tessGenLevel = 64;
2257 			}
2258 			else
2259 			{
2260 				tessGenLevel = 5;
2261 			}
2262 
2263 			m_testCtx.getLog()
2264 					<< tcu::TestLog::Message
2265 					<< "Tessellation level: " << tessGenLevel << ", mode = quad.\n"
2266 					<< "\tEach input patch produces " << (tessGenLevel*tessGenLevel) << " (" << (tessGenLevel*tessGenLevel*2) << " triangles)\n"
2267 					<< tcu::TestLog::EndMessage;
2268 
2269 			sources << glu::TessellationControlSource(getTessellationControlSource(tessGenLevel))
2270 					<< glu::TessellationEvaluationSource(getTessellationEvaluationSource(tessGenLevel));
2271 		}
2272 
2273 		// Geometry limits
2274 		{
2275 			int		geometryOutputComponents		= -1;
2276 			int		geometryOutputVertices			= -1;
2277 			int		geometryTotalOutputComponents	= -1;
2278 			int		geometryShaderInvocations		= -1;
2279 			bool	logGeometryLimits				= false;
2280 			bool	logInvocationLimits				= false;
2281 
2282 			if (m_flags & FLAG_GEOMETRY_MAX_IMPLEMENTATION)
2283 			{
2284 				m_testCtx.getLog() << tcu::TestLog::Message << "Using implementation maximum geometry shader output limits." << tcu::TestLog::EndMessage;
2285 
2286 				gl.getIntegerv(GL_MAX_GEOMETRY_OUTPUT_COMPONENTS, &geometryOutputComponents);
2287 				gl.getIntegerv(GL_MAX_GEOMETRY_OUTPUT_VERTICES, &geometryOutputVertices);
2288 				gl.getIntegerv(GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS, &geometryTotalOutputComponents);
2289 				GLU_EXPECT_NO_ERROR(gl.getError(), "query geometry limits");
2290 
2291 				logGeometryLimits = true;
2292 			}
2293 			else if (m_flags & FLAG_GEOMETRY_MAX_SPEC)
2294 			{
2295 				m_testCtx.getLog() << tcu::TestLog::Message << "Using geometry shader extension minimum maximum output limits." << tcu::TestLog::EndMessage;
2296 
2297 				geometryOutputComponents = 128;
2298 				geometryOutputVertices = 256;
2299 				geometryTotalOutputComponents = 1024;
2300 				logGeometryLimits = true;
2301 			}
2302 			else
2303 			{
2304 				geometryOutputComponents = 128;
2305 				geometryOutputVertices = 16;
2306 				geometryTotalOutputComponents = 1024;
2307 			}
2308 
2309 			if (m_flags & FLAG_GEOMETRY_INVOCATIONS_MAX_IMPLEMENTATION)
2310 			{
2311 				gl.getIntegerv(GL_MAX_GEOMETRY_SHADER_INVOCATIONS, &geometryShaderInvocations);
2312 				GLU_EXPECT_NO_ERROR(gl.getError(), "query geometry invocation limits");
2313 
2314 				logInvocationLimits = true;
2315 			}
2316 			else if (m_flags & FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC)
2317 			{
2318 				geometryShaderInvocations = 32;
2319 				logInvocationLimits = true;
2320 			}
2321 			else
2322 			{
2323 				geometryShaderInvocations = 4;
2324 			}
2325 
2326 			if (logGeometryLimits || logInvocationLimits)
2327 			{
2328 				tcu::MessageBuilder msg(&m_testCtx.getLog());
2329 
2330 				msg << "Geometry shader, targeting following limits:\n";
2331 
2332 				if (logGeometryLimits)
2333 					msg	<< "\tGL_MAX_GEOMETRY_OUTPUT_COMPONENTS = " << geometryOutputComponents << "\n"
2334 						<< "\tGL_MAX_GEOMETRY_OUTPUT_VERTICES = " << geometryOutputVertices << "\n"
2335 						<< "\tGL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS = " << geometryTotalOutputComponents << "\n";
2336 
2337 				if (logInvocationLimits)
2338 					msg << "\tGL_MAX_GEOMETRY_SHADER_INVOCATIONS = " << geometryShaderInvocations;
2339 
2340 				msg << tcu::TestLog::EndMessage;
2341 			}
2342 
2343 			{
2344 				const bool	separatePrimitives			= (m_flags & FLAG_GEOMETRY_SEPARATE_PRIMITIVES) != 0;
2345 				const int	numComponentsPerVertex		= 8; // vec4 pos, vec4 color
2346 				int			numVerticesPerInvocation;
2347 				int			numPrimitivesPerInvocation;
2348 				int			geometryVerticesPerPrimitive;
2349 				int			geometryPrimitivesOutPerPrimitive;
2350 
2351 				if (separatePrimitives)
2352 				{
2353 					const int	numComponentLimit	= geometryTotalOutputComponents / (4 * numComponentsPerVertex);
2354 					const int	numOutputLimit		= geometryOutputVertices / 4;
2355 
2356 					numPrimitivesPerInvocation		= de::min(numComponentLimit, numOutputLimit);
2357 					numVerticesPerInvocation		= numPrimitivesPerInvocation * 4;
2358 				}
2359 				else
2360 				{
2361 					// If FLAG_GEOMETRY_SEPARATE_PRIMITIVES is not set, geometry shader fills a rectangle area in slices.
2362 					// Each slice is a triangle strip and is generated by a single shader invocation.
2363 					// One slice with 4 segment ends (nodes) and 3 segments:
2364 					//    .__.__.__.
2365 					//    |\ |\ |\ |
2366 					//    |_\|_\|_\|
2367 
2368 					const int	numSliceNodesComponentLimit	= geometryTotalOutputComponents / (2 * numComponentsPerVertex);			// each node 2 vertices
2369 					const int	numSliceNodesOutputLimit	= geometryOutputVertices / 2;											// each node 2 vertices
2370 					const int	numSliceNodes				= de::min(numSliceNodesComponentLimit, numSliceNodesOutputLimit);
2371 
2372 					numVerticesPerInvocation				= numSliceNodes * 2;
2373 					numPrimitivesPerInvocation				= (numSliceNodes - 1) * 2;
2374 				}
2375 
2376 				geometryVerticesPerPrimitive = numVerticesPerInvocation * geometryShaderInvocations;
2377 				geometryPrimitivesOutPerPrimitive = numPrimitivesPerInvocation * geometryShaderInvocations;
2378 
2379 				m_testCtx.getLog()
2380 					<< tcu::TestLog::Message
2381 					<< "Geometry shader:\n"
2382 					<< "\tTotal output vertex count per invocation: " << (numVerticesPerInvocation) << "\n"
2383 					<< "\tTotal output primitive count per invocation: " << (numPrimitivesPerInvocation) << "\n"
2384 					<< "\tNumber of invocations per primitive: " << geometryShaderInvocations << "\n"
2385 					<< "\tTotal output vertex count per input primitive: " << (geometryVerticesPerPrimitive) << "\n"
2386 					<< "\tTotal output primitive count per input primitive: " << (geometryPrimitivesOutPerPrimitive) << "\n"
2387 					<< tcu::TestLog::EndMessage;
2388 
2389 				sources	<< glu::GeometrySource(getGeometryShaderSource(numPrimitivesPerInvocation, geometryShaderInvocations, tessGenLevel));
2390 
2391 				m_testCtx.getLog()
2392 					<< tcu::TestLog::Message
2393 					<< "Program:\n"
2394 					<< "\tTotal program output vertices count per input patch: " << (tessGenLevel*tessGenLevel*2 * geometryVerticesPerPrimitive) << "\n"
2395 					<< "\tTotal program output primitive count per input patch: " << (tessGenLevel*tessGenLevel*2 * geometryPrimitivesOutPerPrimitive) << "\n"
2396 					<< tcu::TestLog::EndMessage;
2397 			}
2398 		}
2399 
2400 		m_program = new glu::ShaderProgram(m_context.getRenderContext(), sources);
2401 		m_testCtx.getLog() << *m_program;
2402 		if (!m_program->isOk())
2403 			throw tcu::TestError("failed to build program");
2404 	}
2405 }
2406 
deinit(void)2407 void GridRenderCase::deinit (void)
2408 {
2409 	delete m_program;
2410 	m_program = DE_NULL;
2411 
2412 	if (m_texture)
2413 	{
2414 		m_context.getRenderContext().getFunctions().deleteTextures(1, &m_texture);
2415 		m_texture = 0;
2416 	}
2417 }
2418 
iterate(void)2419 GridRenderCase::IterateResult GridRenderCase::iterate (void)
2420 {
2421 	std::vector<tcu::Surface>	renderedLayers	(m_numLayers);
2422 	bool						allLayersOk		= true;
2423 
2424 	for (int ndx = 0; ndx < m_numLayers; ++ndx)
2425 		renderedLayers[ndx].setSize(RENDER_SIZE, RENDER_SIZE);
2426 
2427 	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;
2428 
2429 	try
2430 	{
2431 		renderTo(renderedLayers);
2432 	}
2433 	catch (const AllowedRenderFailureException& ex)
2434 	{
2435 		// Got accepted failure
2436 		m_testCtx.getLog()
2437 			<< tcu::TestLog::Message
2438 			<< "Could not render, reason: " << ex.what() << "\n"
2439 			<< "Failure is allowed."
2440 			<< tcu::TestLog::EndMessage;
2441 
2442 		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
2443 		return STOP;
2444 	}
2445 
2446 	for (int ndx = 0; ndx < m_numLayers; ++ndx)
2447 		allLayersOk &= verifyResultLayer(ndx, renderedLayers[ndx]);
2448 
2449 	if (allLayersOk)
2450 		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
2451 	else
2452 		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
2453 	return STOP;
2454 }
2455 
renderTo(std::vector<tcu::Surface> & dst)2456 void GridRenderCase::renderTo (std::vector<tcu::Surface>& dst)
2457 {
2458 	const glw::Functions&			gl					= m_context.getRenderContext().getFunctions();
2459 	const int						positionLocation	= gl.getAttribLocation(m_program->getProgram(), "a_position");
2460 	const glu::VertexArray			vao					(m_context.getRenderContext());
2461 	de::MovePtr<glu::Framebuffer>	fbo;
2462 
2463 	if (positionLocation == -1)
2464 		throw tcu::TestError("Attribute a_position location was -1");
2465 
2466 	gl.viewport(0, 0, dst.front().getWidth(), dst.front().getHeight());
2467 	gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
2468 	GLU_EXPECT_NO_ERROR(gl.getError(), "viewport");
2469 
2470 	gl.bindVertexArray(*vao);
2471 	GLU_EXPECT_NO_ERROR(gl.getError(), "bind vao");
2472 
2473 	gl.useProgram(m_program->getProgram());
2474 	GLU_EXPECT_NO_ERROR(gl.getError(), "use program");
2475 
2476 	gl.patchParameteri(GL_PATCH_VERTICES, 1);
2477 	GLU_EXPECT_NO_ERROR(gl.getError(), "set patch param");
2478 
2479 	gl.vertexAttrib4f(positionLocation, 0.0f, 0.0f, 0.0f, 1.0f);
2480 
2481 	if (m_flags & FLAG_GEOMETRY_SCATTER_LAYERS)
2482 	{
2483 		// clear texture contents
2484 		{
2485 			glu::Framebuffer clearFbo(m_context.getRenderContext());
2486 			gl.bindFramebuffer(GL_FRAMEBUFFER, *clearFbo);
2487 
2488 			for (int layerNdx = 0; layerNdx < m_numLayers; ++layerNdx)
2489 			{
2490 				gl.framebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_texture, 0, layerNdx);
2491 				gl.clear(GL_COLOR_BUFFER_BIT);
2492 			}
2493 
2494 			GLU_EXPECT_NO_ERROR(gl.getError(), "clear tex contents");
2495 		}
2496 
2497 		// create and bind layered fbo
2498 
2499 		fbo = de::MovePtr<glu::Framebuffer>(new glu::Framebuffer(m_context.getRenderContext()));
2500 
2501 		gl.bindFramebuffer(GL_FRAMEBUFFER, **fbo);
2502 		gl.framebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_texture, 0);
2503 		GLU_EXPECT_NO_ERROR(gl.getError(), "gen fbo");
2504 	}
2505 	else
2506 	{
2507 		// clear viewport
2508 		gl.clear(GL_COLOR_BUFFER_BIT);
2509 	}
2510 
2511 	// draw
2512 	{
2513 		glw::GLenum glerror;
2514 
2515 		gl.drawArrays(GL_PATCHES, 0, 1);
2516 
2517 		glerror = gl.getError();
2518 		if (glerror == GL_OUT_OF_MEMORY && (m_flags & FLAG_ALLOW_OUT_OF_MEMORY))
2519 			throw AllowedRenderFailureException("got GL_OUT_OF_MEMORY while drawing");
2520 
2521 		GLU_EXPECT_NO_ERROR(glerror, "draw patches");
2522 	}
2523 
2524 	// Read layers
2525 
2526 	if (m_flags & FLAG_GEOMETRY_SCATTER_LAYERS)
2527 	{
2528 		glu::Framebuffer readFbo(m_context.getRenderContext());
2529 		gl.bindFramebuffer(GL_FRAMEBUFFER, *readFbo);
2530 
2531 		for (int layerNdx = 0; layerNdx < m_numLayers; ++layerNdx)
2532 		{
2533 			gl.framebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_texture, 0, layerNdx);
2534 			glu::readPixels(m_context.getRenderContext(), 0, 0, dst[layerNdx].getAccess());
2535 			GLU_EXPECT_NO_ERROR(gl.getError(), "read pixels");
2536 		}
2537 	}
2538 	else
2539 	{
2540 		glu::readPixels(m_context.getRenderContext(), 0, 0, dst.front().getAccess());
2541 		GLU_EXPECT_NO_ERROR(gl.getError(), "read pixels");
2542 	}
2543 }
2544 
verifyResultLayer(int layerNdx,const tcu::Surface & image)2545 bool GridRenderCase::verifyResultLayer (int layerNdx, const tcu::Surface& image)
2546 {
2547 	tcu::Surface	errorMask	(image.getWidth(), image.getHeight());
2548 	bool			foundError	= false;
2549 
2550 	tcu::clear(errorMask.getAccess(), tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f));
2551 
2552 	m_testCtx.getLog() << tcu::TestLog::Message << "Verifying output layer " << layerNdx  << tcu::TestLog::EndMessage;
2553 
2554 	for (int y = 0; y < image.getHeight(); ++y)
2555 	for (int x = 0; x < image.getWidth(); ++x)
2556 	{
2557 		const int		threshold	= 8;
2558 		const tcu::RGBA	color		= image.getPixel(x, y);
2559 
2560 		// Color must be a linear combination of green and yellow
2561 		if (color.getGreen() < 255 - threshold || color.getBlue() > threshold)
2562 		{
2563 			errorMask.setPixel(x, y, tcu::RGBA::red());
2564 			foundError = true;
2565 		}
2566 	}
2567 
2568 	if (!foundError)
2569 	{
2570 		m_testCtx.getLog()
2571 			<< tcu::TestLog::Message << "Image valid." << tcu::TestLog::EndMessage
2572 			<< tcu::TestLog::ImageSet("ImageVerification", "Image verification")
2573 			<< tcu::TestLog::Image("Result", "Rendered result", image.getAccess())
2574 			<< tcu::TestLog::EndImageSet;
2575 		return true;
2576 	}
2577 	else
2578 	{
2579 		m_testCtx.getLog()
2580 			<< tcu::TestLog::Message << "Image verification failed, found invalid pixels." << tcu::TestLog::EndMessage
2581 			<< tcu::TestLog::ImageSet("ImageVerification", "Image verification")
2582 			<< tcu::TestLog::Image("Result", "Rendered result", image.getAccess())
2583 			<< tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess())
2584 			<< tcu::TestLog::EndImageSet;
2585 		return false;
2586 	}
2587 }
2588 
getVertexSource(void)2589 std::string GridRenderCase::getVertexSource (void)
2590 {
2591 	return specializeShader(s_positionVertexShader, m_context.getRenderContext().getType());
2592 }
2593 
getFragmentSource(void)2594 std::string GridRenderCase::getFragmentSource (void)
2595 {
2596 	const char* source = "${VERSION_DECL}\n"
2597 						 "flat in mediump vec4 v_color;\n"
2598 						 "layout(location = 0) out mediump vec4 fragColor;\n"
2599 						 "void main (void)\n"
2600 						 "{\n"
2601 						 "	fragColor = v_color;\n"
2602 						 "}\n";
2603 
2604 	return specializeShader(source, m_context.getRenderContext().getType());
2605 }
2606 
getTessellationControlSource(int tessLevel)2607 std::string GridRenderCase::getTessellationControlSource (int tessLevel)
2608 {
2609 	std::ostringstream buf;
2610 
2611 	buf <<	"${VERSION_DECL}\n"
2612 			"${EXTENSION_TESSELATION_SHADER}"
2613 			"layout(vertices=1) out;\n"
2614 			"\n"
2615 			"void main()\n"
2616 			"{\n"
2617 			"	gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
2618 			"	gl_TessLevelOuter[0] = " << tessLevel << ".0;\n"
2619 			"	gl_TessLevelOuter[1] = " << tessLevel << ".0;\n"
2620 			"	gl_TessLevelOuter[2] = " << tessLevel << ".0;\n"
2621 			"	gl_TessLevelOuter[3] = " << tessLevel << ".0;\n"
2622 			"	gl_TessLevelInner[0] = " << tessLevel << ".0;\n"
2623 			"	gl_TessLevelInner[1] = " << tessLevel << ".0;\n"
2624 			"}\n";
2625 
2626 	return specializeShader(buf.str(), m_context.getRenderContext().getType());
2627 }
2628 
getTessellationEvaluationSource(int tessLevel)2629 std::string GridRenderCase::getTessellationEvaluationSource (int tessLevel)
2630 {
2631 	std::ostringstream buf;
2632 
2633 	buf <<	"${VERSION_DECL}\n"
2634 			"${EXTENSION_TESSELATION_SHADER}"
2635 			"layout(quads) in;\n"
2636 			"\n"
2637 			"out mediump ivec2 v_tessellationGridPosition;\n"
2638 			"\n"
2639 			"// note: No need to use precise gl_Position since position does not depend on order\n"
2640 			"void main (void)\n"
2641 			"{\n";
2642 
2643 	if (m_flags & (FLAG_GEOMETRY_SCATTER_INSTANCES | FLAG_GEOMETRY_SCATTER_PRIMITIVES | FLAG_GEOMETRY_SCATTER_LAYERS))
2644 		buf <<	"	// Cover only a small area in a corner. The area will be expanded in geometry shader to cover whole viewport\n"
2645 				"	gl_Position = vec4(gl_TessCoord.x * 0.3 - 1.0, gl_TessCoord.y * 0.3 - 1.0, 0.0, 1.0);\n";
2646 	else
2647 		buf <<	"	// Fill the whole viewport\n"
2648 				"	gl_Position = vec4(gl_TessCoord.x * 2.0 - 1.0, gl_TessCoord.y * 2.0 - 1.0, 0.0, 1.0);\n";
2649 
2650 	buf <<	"	// Calculate position in tessellation grid\n"
2651 			"	v_tessellationGridPosition = ivec2(round(gl_TessCoord.xy * float(" << tessLevel << ")));\n"
2652 			"}\n";
2653 
2654 	return specializeShader(buf.str(), m_context.getRenderContext().getType());
2655 }
2656 
getGeometryShaderSource(int numPrimitives,int numInstances,int tessLevel)2657 std::string GridRenderCase::getGeometryShaderSource (int numPrimitives, int numInstances, int tessLevel)
2658 {
2659 	std::ostringstream buf;
2660 
2661 	buf	<<	"${VERSION_DECL}\n"
2662 			"${EXTENSION_GEOMETRY_SHADER}"
2663 			"layout(triangles, invocations=" << numInstances << ") in;\n"
2664 			"layout(triangle_strip, max_vertices=" << ((m_flags & FLAG_GEOMETRY_SEPARATE_PRIMITIVES) ? (4 * numPrimitives) : (numPrimitives + 2)) << ") out;\n"
2665 			"\n"
2666 			"in mediump ivec2 v_tessellationGridPosition[];\n"
2667 			"flat out highp vec4 v_color;\n"
2668 			"\n"
2669 			"void main ()\n"
2670 			"{\n"
2671 			"	const float equalThreshold = 0.001;\n"
2672 			"	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"
2673 			"\n"
2674 			"	// Input triangle is generated from an axis-aligned rectangle by splitting it in half\n"
2675 			"	// Original rectangle can be found by finding the bounding AABB of the triangle\n"
2676 			"	vec4 aabb = vec4(min(gl_in[0].gl_Position.x, min(gl_in[1].gl_Position.x, gl_in[2].gl_Position.x)),\n"
2677 			"	                 min(gl_in[0].gl_Position.y, min(gl_in[1].gl_Position.y, gl_in[2].gl_Position.y)),\n"
2678 			"	                 max(gl_in[0].gl_Position.x, max(gl_in[1].gl_Position.x, gl_in[2].gl_Position.x)),\n"
2679 			"	                 max(gl_in[0].gl_Position.y, max(gl_in[1].gl_Position.y, gl_in[2].gl_Position.y)));\n"
2680 			"\n"
2681 			"	// Location in tessellation grid\n"
2682 			"	ivec2 gridPosition = ivec2(min(v_tessellationGridPosition[0], min(v_tessellationGridPosition[1], v_tessellationGridPosition[2])));\n"
2683 			"\n"
2684 			"	// Which triangle of the two that split the grid cell\n"
2685 			"	int numVerticesOnBottomEdge = 0;\n"
2686 			"	for (int ndx = 0; ndx < 3; ++ndx)\n"
2687 			"		if (abs(gl_in[ndx].gl_Position.y - aabb.w) < equalThreshold)\n"
2688 			"			++numVerticesOnBottomEdge;\n"
2689 			"	bool isBottomTriangle = numVerticesOnBottomEdge == 2;\n"
2690 			"\n";
2691 
2692 	if (m_flags & FLAG_GEOMETRY_SCATTER_PRIMITIVES)
2693 	{
2694 		// scatter primitives
2695 		buf <<	"	// Draw grid cells\n"
2696 				"	int inputTriangleNdx = gl_InvocationID * 2 + ((isBottomTriangle) ? (1) : (0));\n"
2697 				"	for (int ndx = 0; ndx < " << numPrimitives << "; ++ndx)\n"
2698 				"	{\n"
2699 				"		ivec2 dstGridSize = ivec2(" << tessLevel << " * " << numPrimitives << ", 2 * " << tessLevel << " * " << numInstances << ");\n"
2700 				"		ivec2 dstGridNdx = ivec2(" << tessLevel << " * ndx + gridPosition.x, " << tessLevel << " * inputTriangleNdx + 2 * gridPosition.y + ndx * 127) % dstGridSize;\n"
2701 				"		vec4 dstArea;\n"
2702 				"		dstArea.x = float(dstGridNdx.x) / float(dstGridSize.x) * 2.0 - 1.0 - gapOffset;\n"
2703 				"		dstArea.y = float(dstGridNdx.y) / float(dstGridSize.y) * 2.0 - 1.0 - gapOffset;\n"
2704 				"		dstArea.z = float(dstGridNdx.x+1) / float(dstGridSize.x) * 2.0 - 1.0 + gapOffset;\n"
2705 				"		dstArea.w = float(dstGridNdx.y+1) / float(dstGridSize.y) * 2.0 - 1.0 + gapOffset;\n"
2706 				"\n"
2707 				"		vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n"
2708 				"		vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n"
2709 				"		vec4 outputColor = (((dstGridNdx.y + dstGridNdx.x) % 2) == 0) ? (green) : (yellow);\n"
2710 				"\n"
2711 				"		gl_Position = vec4(dstArea.x, dstArea.y, 0.0, 1.0);\n"
2712 				"		v_color = outputColor;\n"
2713 				"		EmitVertex();\n"
2714 				"\n"
2715 				"		gl_Position = vec4(dstArea.x, dstArea.w, 0.0, 1.0);\n"
2716 				"		v_color = outputColor;\n"
2717 				"		EmitVertex();\n"
2718 				"\n"
2719 				"		gl_Position = vec4(dstArea.z, dstArea.y, 0.0, 1.0);\n"
2720 				"		v_color = outputColor;\n"
2721 				"		EmitVertex();\n"
2722 				"\n"
2723 				"		gl_Position = vec4(dstArea.z, dstArea.w, 0.0, 1.0);\n"
2724 				"		v_color = outputColor;\n"
2725 				"		EmitVertex();\n"
2726 				"		EndPrimitive();\n"
2727 				"	}\n";
2728 	}
2729 	else if (m_flags & FLAG_GEOMETRY_SCATTER_LAYERS)
2730 	{
2731 		// Number of subrectangle instances = num layers
2732 		DE_ASSERT(m_numLayers == numInstances * 2);
2733 
2734 		buf <<	"	// Draw grid cells, send each primitive to a separate layer\n"
2735 				"	int baseLayer = gl_InvocationID * 2 + ((isBottomTriangle) ? (1) : (0));\n"
2736 				"	for (int ndx = 0; ndx < " << numPrimitives << "; ++ndx)\n"
2737 				"	{\n"
2738 				"		ivec2 dstGridSize = ivec2(" << tessLevel << " * " << numPrimitives << ", " << tessLevel << ");\n"
2739 				"		ivec2 dstGridNdx = ivec2((gridPosition.x * " << numPrimitives << " * 7 + ndx)*13, (gridPosition.y * 127 + ndx) * 19) % dstGridSize;\n"
2740 				"		vec4 dstArea;\n"
2741 				"		dstArea.x = float(dstGridNdx.x) / float(dstGridSize.x) * 2.0 - 1.0 - gapOffset;\n"
2742 				"		dstArea.y = float(dstGridNdx.y) / float(dstGridSize.y) * 2.0 - 1.0 - gapOffset;\n"
2743 				"		dstArea.z = float(dstGridNdx.x+1) / float(dstGridSize.x) * 2.0 - 1.0 + gapOffset;\n"
2744 				"		dstArea.w = float(dstGridNdx.y+1) / float(dstGridSize.y) * 2.0 - 1.0 + gapOffset;\n"
2745 				"\n"
2746 				"		vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n"
2747 				"		vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n"
2748 				"		vec4 outputColor = (((dstGridNdx.y + dstGridNdx.x) % 2) == 0) ? (green) : (yellow);\n"
2749 				"\n"
2750 				"		gl_Position = vec4(dstArea.x, dstArea.y, 0.0, 1.0);\n"
2751 				"		v_color = outputColor;\n"
2752 				"		gl_Layer = ((baseLayer + ndx) * 11) % " << m_numLayers << ";\n"
2753 				"		EmitVertex();\n"
2754 				"\n"
2755 				"		gl_Position = vec4(dstArea.x, dstArea.w, 0.0, 1.0);\n"
2756 				"		v_color = outputColor;\n"
2757 				"		gl_Layer = ((baseLayer + ndx) * 11) % " << m_numLayers << ";\n"
2758 				"		EmitVertex();\n"
2759 				"\n"
2760 				"		gl_Position = vec4(dstArea.z, dstArea.y, 0.0, 1.0);\n"
2761 				"		v_color = outputColor;\n"
2762 				"		gl_Layer = ((baseLayer + ndx) * 11) % " << m_numLayers << ";\n"
2763 				"		EmitVertex();\n"
2764 				"\n"
2765 				"		gl_Position = vec4(dstArea.z, dstArea.w, 0.0, 1.0);\n"
2766 				"		v_color = outputColor;\n"
2767 				"		gl_Layer = ((baseLayer + ndx) * 11) % " << m_numLayers << ";\n"
2768 				"		EmitVertex();\n"
2769 				"		EndPrimitive();\n"
2770 				"	}\n";
2771 	}
2772 	else
2773 	{
2774 		if (m_flags & FLAG_GEOMETRY_SCATTER_INSTANCES)
2775 		{
2776 			buf <<	"	// Scatter slices\n"
2777 					"	int inputTriangleNdx = gl_InvocationID * 2 + ((isBottomTriangle) ? (1) : (0));\n"
2778 					"	ivec2 srcSliceNdx = ivec2(gridPosition.x, gridPosition.y * " << (numInstances*2) << " + inputTriangleNdx);\n"
2779 					"	ivec2 dstSliceNdx = ivec2(7 * srcSliceNdx.x, 127 * srcSliceNdx.y) % ivec2(" << tessLevel << ", " << tessLevel << " * " << (numInstances*2) << ");\n"
2780 					"\n"
2781 					"	// Draw slice to the dstSlice slot\n"
2782 					"	vec4 outputSliceArea;\n"
2783 					"	outputSliceArea.x = float(dstSliceNdx.x) / float(" << tessLevel << ") * 2.0 - 1.0 - gapOffset;\n"
2784 					"	outputSliceArea.y = float(dstSliceNdx.y) / float(" << (tessLevel * numInstances * 2) << ") * 2.0 - 1.0 - gapOffset;\n"
2785 					"	outputSliceArea.z = float(dstSliceNdx.x+1) / float(" << tessLevel << ") * 2.0 - 1.0 + gapOffset;\n"
2786 					"	outputSliceArea.w = float(dstSliceNdx.y+1) / float(" << (tessLevel * numInstances * 2) << ") * 2.0 - 1.0 + gapOffset;\n";
2787 		}
2788 		else
2789 		{
2790 			buf <<	"	// Fill the input area with slices\n"
2791 					"	// Upper triangle produces slices only to the upper half of the quad and vice-versa\n"
2792 					"	float triangleOffset = (isBottomTriangle) ? ((aabb.w + aabb.y) / 2.0) : (aabb.y);\n"
2793 					"	// Each slice is a invocation\n"
2794 					"	float sliceHeight = (aabb.w - aabb.y) / float(2 * " << numInstances << ");\n"
2795 					"	float invocationOffset = float(gl_InvocationID) * sliceHeight;\n"
2796 					"\n"
2797 					"	vec4 outputSliceArea;\n"
2798 					"	outputSliceArea.x = aabb.x - gapOffset;\n"
2799 					"	outputSliceArea.y = triangleOffset + invocationOffset - gapOffset;\n"
2800 					"	outputSliceArea.z = aabb.z + gapOffset;\n"
2801 					"	outputSliceArea.w = triangleOffset + invocationOffset + sliceHeight + gapOffset;\n";
2802 		}
2803 
2804 		buf <<	"\n"
2805 				"	// Draw slice\n"
2806 				"	for (int ndx = 0; ndx < " << ((numPrimitives+2)/2) << "; ++ndx)\n"
2807 				"	{\n"
2808 				"		vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n"
2809 				"		vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n"
2810 				"		vec4 outputColor = (((gl_InvocationID + ndx) % 2) == 0) ? (green) : (yellow);\n"
2811 				"		float xpos = mix(outputSliceArea.x, outputSliceArea.z, float(ndx) / float(" << (numPrimitives/2) << "));\n"
2812 				"\n"
2813 				"		gl_Position = vec4(xpos, outputSliceArea.y, 0.0, 1.0);\n"
2814 				"		v_color = outputColor;\n"
2815 				"		EmitVertex();\n"
2816 				"\n"
2817 				"		gl_Position = vec4(xpos, outputSliceArea.w, 0.0, 1.0);\n"
2818 				"		v_color = outputColor;\n"
2819 				"		EmitVertex();\n"
2820 				"	}\n";
2821 	}
2822 
2823 	buf <<	"}\n";
2824 
2825 	return specializeShader(buf.str(), m_context.getRenderContext().getType());
2826 }
2827 
2828 class FeedbackRecordVariableSelectionCase : public TestCase
2829 {
2830 public:
2831 						FeedbackRecordVariableSelectionCase		(Context& context, const char* name, const char* description);
2832 						~FeedbackRecordVariableSelectionCase	(void);
2833 
2834 private:
2835 	void				init									(void);
2836 	void				deinit									(void);
2837 	IterateResult		iterate									(void);
2838 
2839 	std::string			getVertexSource							(void);
2840 	std::string			getFragmentSource						(void);
2841 	std::string			getTessellationControlSource			(void);
2842 	std::string			getTessellationEvaluationSource			(void);
2843 	std::string			getGeometrySource						(void);
2844 
2845 	glu::ShaderProgram*	m_program;
2846 	deUint32			m_xfbBuf;
2847 };
2848 
FeedbackRecordVariableSelectionCase(Context & context,const char * name,const char * description)2849 FeedbackRecordVariableSelectionCase::FeedbackRecordVariableSelectionCase (Context& context, const char* name, const char* description)
2850 	: TestCase	(context, name, description)
2851 	, m_program	(DE_NULL)
2852 	, m_xfbBuf	(0)
2853 {
2854 }
2855 
~FeedbackRecordVariableSelectionCase(void)2856 FeedbackRecordVariableSelectionCase::~FeedbackRecordVariableSelectionCase (void)
2857 {
2858 	deinit();
2859 }
2860 
init(void)2861 void FeedbackRecordVariableSelectionCase::init (void)
2862 {
2863 	const bool supportsES32 = glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2));
2864 	const bool supportsCore40 = glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 0));
2865 
2866 	if ((!supportsES32 && !supportsCore40) &&
2867 		(!m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader") ||
2868 		 !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader")))
2869 		throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader and GL_EXT_geometry_shader extensions");
2870 
2871 	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;
2872 
2873 	// gen feedback buffer fit for 1 triangle (4 components)
2874 	{
2875 		static const tcu::Vec4 initialData[3] =
2876 		{
2877 			tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.0f),
2878 			tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.0f),
2879 			tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.0f),
2880 		};
2881 
2882 		const glw::Functions& gl = m_context.getRenderContext().getFunctions();
2883 
2884 		m_testCtx.getLog() << tcu::TestLog::Message << "Creating buffer for transform feedback. Allocating storage for one triangle. Filling with -1.0" << tcu::TestLog::EndMessage;
2885 
2886 		gl.genBuffers(1, &m_xfbBuf);
2887 		gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_xfbBuf);
2888 		gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, (int)(sizeof(tcu::Vec4[3])), initialData, GL_DYNAMIC_READ);
2889 		GLU_EXPECT_NO_ERROR(gl.getError(), "gen xfb buf");
2890 	}
2891 
2892 	// gen shader
2893 	m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
2894 																	 << glu::VertexSource(getVertexSource())
2895 																	 << glu::FragmentSource(getFragmentSource())
2896 																	 << glu::TessellationControlSource(getTessellationControlSource())
2897 																	 << glu::TessellationEvaluationSource(getTessellationEvaluationSource())
2898 																	 << glu::GeometrySource(getGeometrySource())
2899 																	 << glu::TransformFeedbackMode(GL_INTERLEAVED_ATTRIBS)
2900 																	 << glu::TransformFeedbackVarying("tf_feedback"));
2901 	m_testCtx.getLog() << *m_program;
2902 
2903 	if (!m_program->isOk())
2904 		throw tcu::TestError("could not build program");
2905 }
2906 
deinit(void)2907 void FeedbackRecordVariableSelectionCase::deinit (void)
2908 {
2909 	delete m_program;
2910 	m_program = DE_NULL;
2911 
2912 	if (m_xfbBuf)
2913 	{
2914 		m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_xfbBuf);
2915 		m_xfbBuf = 0;
2916 	}
2917 }
2918 
iterate(void)2919 FeedbackRecordVariableSelectionCase::IterateResult FeedbackRecordVariableSelectionCase::iterate (void)
2920 {
2921 	const glw::Functions&	gl		= m_context.getRenderContext().getFunctions();
2922 	const int				posLoc	= gl.getAttribLocation(m_program->getProgram(), "a_position");
2923 	const glu::VertexArray	vao		(m_context.getRenderContext());
2924 
2925 	if (posLoc == -1)
2926 		throw tcu::TestError("a_position attribute location was -1");
2927 
2928 	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
2929 
2930 	m_testCtx.getLog() << tcu::TestLog::Message << "Rendering a patch of size 3." << tcu::TestLog::EndMessage;
2931 
2932 	// Render and feed back
2933 
2934 	gl.viewport(0, 0, 1, 1);
2935 	gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
2936 	gl.clear(GL_COLOR_BUFFER_BIT);
2937 	GLU_EXPECT_NO_ERROR(gl.getError(), "clear");
2938 
2939 	gl.bindVertexArray(*vao);
2940 	GLU_EXPECT_NO_ERROR(gl.getError(), "bindVertexArray");
2941 
2942 	gl.vertexAttrib4f(posLoc, 0.0f, 0.0f, 0.0f, 1.0f);
2943 	GLU_EXPECT_NO_ERROR(gl.getError(), "vertexAttrib4f");
2944 
2945 	gl.useProgram(m_program->getProgram());
2946 	GLU_EXPECT_NO_ERROR(gl.getError(), "use program");
2947 
2948 	gl.patchParameteri(GL_PATCH_VERTICES, 3);
2949 	GLU_EXPECT_NO_ERROR(gl.getError(), "set patch param");
2950 
2951 	gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_xfbBuf);
2952 	GLU_EXPECT_NO_ERROR(gl.getError(), "bind xfb buf");
2953 
2954 	gl.beginTransformFeedback(GL_TRIANGLES);
2955 	GLU_EXPECT_NO_ERROR(gl.getError(), "beginTransformFeedback");
2956 
2957 	gl.drawArrays(GL_PATCHES, 0, 3);
2958 	GLU_EXPECT_NO_ERROR(gl.getError(), "drawArrays");
2959 
2960 	gl.endTransformFeedback();
2961 	GLU_EXPECT_NO_ERROR(gl.getError(), "beginTransformFeedback");
2962 
2963 	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;
2964 
2965 	// Read back result (one triangle)
2966 	{
2967 		tcu::Vec4	feedbackValues[3];
2968 		const void* mapPtr				= gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, (int)sizeof(feedbackValues), GL_MAP_READ_BIT);
2969 		GLU_EXPECT_NO_ERROR(gl.getError(), "mapBufferRange");
2970 
2971 		if (mapPtr == DE_NULL)
2972 			throw tcu::TestError("mapBufferRange returned null");
2973 
2974 		deMemcpy(feedbackValues, mapPtr, sizeof(feedbackValues));
2975 
2976 		if (gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER) != GL_TRUE)
2977 			throw tcu::TestError("unmapBuffer did not return TRUE");
2978 
2979 		for (int ndx = 0; ndx < 3; ++ndx)
2980 		{
2981 			if (!tcu::boolAll(tcu::lessThan(tcu::abs(feedbackValues[ndx] - tcu::Vec4(3.0f)), tcu::Vec4(0.001f))))
2982 			{
2983 				m_testCtx.getLog() << tcu::TestLog::Message << "Feedback vertex " << ndx << ": expected (3.0, 3.0, 3.0, 3.0), got " << feedbackValues[ndx] << tcu::TestLog::EndMessage;
2984 				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got unexpected feedback results");
2985 			}
2986 		}
2987 	}
2988 
2989 	return STOP;
2990 }
2991 
getVertexSource(void)2992 std::string FeedbackRecordVariableSelectionCase::getVertexSource (void)
2993 {
2994 	std::string source =	"${VERSION_DECL}\n"
2995 							"in highp vec4 a_position;\n"
2996 							"out highp vec4 tf_feedback;\n"
2997 							"void main()\n"
2998 							"{\n"
2999 							"	gl_Position = a_position;\n"
3000 							"	tf_feedback = vec4(1.0, 1.0, 1.0, 1.0);\n"
3001 							"}\n";
3002 
3003 	return specializeShader(source, m_context.getRenderContext().getType());
3004 }
3005 
getFragmentSource(void)3006 std::string FeedbackRecordVariableSelectionCase::getFragmentSource (void)
3007 {
3008 	return specializeShader(s_whiteOutputFragmentShader, m_context.getRenderContext().getType());
3009 }
3010 
getTessellationControlSource(void)3011 std::string FeedbackRecordVariableSelectionCase::getTessellationControlSource (void)
3012 {
3013 	std::string source =	"${VERSION_DECL}\n"
3014 							"${EXTENSION_TESSELATION_SHADER}"
3015 							"layout(vertices=3) out;\n"
3016 							"void main()\n"
3017 							"{\n"
3018 							"	gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
3019 							"	gl_TessLevelOuter[0] = 1.0;\n"
3020 							"	gl_TessLevelOuter[1] = 1.0;\n"
3021 							"	gl_TessLevelOuter[2] = 1.0;\n"
3022 							"	gl_TessLevelInner[0] = 1.0;\n"
3023 							"}\n";
3024 
3025 	return specializeShader(source, m_context.getRenderContext().getType());
3026 }
3027 
getTessellationEvaluationSource(void)3028 std::string FeedbackRecordVariableSelectionCase::getTessellationEvaluationSource (void)
3029 {
3030 	std::string source =	"${VERSION_DECL}\n"
3031 							"${EXTENSION_TESSELATION_SHADER}"
3032 							"layout(triangles) in;\n"
3033 							"out highp vec4 tf_feedback;\n"
3034 							"void main()\n"
3035 							"{\n"
3036 							"	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"
3037 							"	tf_feedback = vec4(2.0, 2.0, 2.0, 2.0);\n"
3038 							"}\n";
3039 
3040 	return specializeShader(source, m_context.getRenderContext().getType());
3041 }
3042 
getGeometrySource(void)3043 std::string FeedbackRecordVariableSelectionCase::getGeometrySource(void)
3044 {
3045 	std::string source =	"${VERSION_DECL}\n"
3046 							"${EXTENSION_GEOMETRY_SHADER}"
3047 							"layout (triangles) in;\n"
3048 							"layout (triangle_strip, max_vertices=3) out;\n"
3049 							"out highp vec4 tf_feedback;\n"
3050 							"void main()\n"
3051 							"{\n"
3052 							"	for (int ndx = 0; ndx < 3; ++ndx)\n"
3053 							"	{\n"
3054 							"		gl_Position = gl_in[ndx].gl_Position + vec4(float(ndx), float(ndx)*float(ndx), 0.0, 0.0);\n"
3055 							"		tf_feedback = vec4(3.0, 3.0, 3.0, 3.0);\n"
3056 							"		EmitVertex();\n"
3057 							"	}\n"
3058 							"	EndPrimitive();\n"
3059 							"}\n";
3060 
3061 	return specializeShader(source, m_context.getRenderContext().getType());
3062 }
3063 
3064 } // anonymous
3065 
TessellationGeometryInteractionTests(Context & context,bool isGL45)3066 TessellationGeometryInteractionTests::TessellationGeometryInteractionTests (Context& context, bool isGL45)
3067 	: TestCaseGroup(context, "tessellation_geometry_interaction", "Tessellation and geometry shader interaction tests")
3068 	, m_isGL45(isGL45)
3069 {
3070 }
3071 
~TessellationGeometryInteractionTests(void)3072 TessellationGeometryInteractionTests::~TessellationGeometryInteractionTests (void)
3073 {
3074 }
3075 
init(void)3076 void TessellationGeometryInteractionTests::init (void)
3077 {
3078 	tcu::TestCaseGroup* const renderGroup		= new tcu::TestCaseGroup(m_testCtx, "render",		"Various render tests");
3079 	tcu::TestCaseGroup* const feedbackGroup		= new tcu::TestCaseGroup(m_testCtx, "feedback",		"Test transform feedback");
3080 	tcu::TestCaseGroup* const pointSizeGroup	= new tcu::TestCaseGroup(m_testCtx, "point_size",	"Test point size");
3081 
3082 	addChild(renderGroup);
3083 	addChild(feedbackGroup);
3084 	addChild(pointSizeGroup);
3085 
3086 	// .render
3087 	{
3088 		tcu::TestCaseGroup* const passthroughGroup	= new tcu::TestCaseGroup(m_testCtx, "passthrough",	"Render various types with either passthrough geometry or tessellation shader");
3089 		tcu::TestCaseGroup* const limitGroup		= new tcu::TestCaseGroup(m_testCtx, "limits",		"Render with properties near their limits");
3090 		tcu::TestCaseGroup* const scatterGroup		= new tcu::TestCaseGroup(m_testCtx, "scatter",		"Scatter output primitives");
3091 
3092 		renderGroup->addChild(passthroughGroup);
3093 		renderGroup->addChild(limitGroup);
3094 		renderGroup->addChild(scatterGroup);
3095 
3096 		// .passthrough
3097 		{
3098 			// tessellate_tris_passthrough_geometry_no_change
3099 			// tessellate_quads_passthrough_geometry_no_change
3100 			// tessellate_isolines_passthrough_geometry_no_change
3101 			passthroughGroup->addChild(new IdentityGeometryShaderCase(m_context, "tessellate_tris_passthrough_geometry_no_change",		"Passthrough geometry shader has no effect", IdentityGeometryShaderCase::CASE_TRIANGLES));
3102 			passthroughGroup->addChild(new IdentityGeometryShaderCase(m_context, "tessellate_quads_passthrough_geometry_no_change",		"Passthrough geometry shader has no effect", IdentityGeometryShaderCase::CASE_QUADS));
3103 			passthroughGroup->addChild(new IdentityGeometryShaderCase(m_context, "tessellate_isolines_passthrough_geometry_no_change",	"Passthrough geometry shader has no effect", IdentityGeometryShaderCase::CASE_ISOLINES));
3104 
3105 			// passthrough_tessellation_geometry_shade_triangles_no_change
3106 			// passthrough_tessellation_geometry_shade_lines_no_change
3107 			passthroughGroup->addChild(new IdentityTessellationShaderCase(m_context, "passthrough_tessellation_geometry_shade_triangles_no_change",	"Passthrough tessellation shader has no effect", IdentityTessellationShaderCase::CASE_TRIANGLES));
3108 			passthroughGroup->addChild(new IdentityTessellationShaderCase(m_context, "passthrough_tessellation_geometry_shade_lines_no_change",		"Passthrough tessellation shader has no effect", IdentityTessellationShaderCase::CASE_ISOLINES));
3109 		}
3110 
3111 		// .limits
3112 		{
3113 			static const struct LimitCaseDef
3114 			{
3115 				const char*	name;
3116 				const char*	desc;
3117 				int			flags;
3118 			} cases[] =
3119 			{
3120 				// Test single limit
3121 				{
3122 					"output_required_max_tessellation",
3123 					"Minimum maximum tessellation level",
3124 					GridRenderCase::FLAG_TESSELLATION_MAX_SPEC
3125 				},
3126 				{
3127 					"output_implementation_max_tessellation",
3128 					"Maximum tessellation level supported by the implementation",
3129 					GridRenderCase::FLAG_TESSELLATION_MAX_IMPLEMENTATION
3130 				},
3131 				{
3132 					"output_required_max_geometry",
3133 					"Output minimum maximum number of vertices the geometry shader",
3134 					GridRenderCase::FLAG_GEOMETRY_MAX_SPEC
3135 				},
3136 				{
3137 					"output_implementation_max_geometry",
3138 					"Output maximum number of vertices in the geometry shader supported by the implementation",
3139 					GridRenderCase::FLAG_GEOMETRY_MAX_IMPLEMENTATION
3140 				},
3141 				{
3142 					"output_required_max_invocations",
3143 					"Minimum maximum number of geometry shader invocations",
3144 					GridRenderCase::FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC
3145 				},
3146 				{
3147 					"output_implementation_max_invocations",
3148 					"Maximum number of geometry shader invocations supported by the implementation",
3149 					GridRenderCase::FLAG_GEOMETRY_INVOCATIONS_MAX_IMPLEMENTATION
3150 				},
3151 			};
3152 
3153 			for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(cases); ++ndx)
3154 				limitGroup->addChild(new GridRenderCase(m_context, cases[ndx].name, cases[ndx].desc, cases[ndx].flags));
3155 		}
3156 
3157 		// .scatter
3158 		{
3159 			scatterGroup->addChild(new GridRenderCase(m_context,
3160 													  "geometry_scatter_instances",
3161 													  "Each geometry shader instance outputs its primitives far from other instances of the same execution",
3162 													  GridRenderCase::FLAG_GEOMETRY_SCATTER_INSTANCES));
3163 			scatterGroup->addChild(new GridRenderCase(m_context,
3164 													  "geometry_scatter_primitives",
3165 													  "Each geometry shader instance outputs its primitives far from other primitives of the same instance",
3166 													  GridRenderCase::FLAG_GEOMETRY_SCATTER_PRIMITIVES | GridRenderCase::FLAG_GEOMETRY_SEPARATE_PRIMITIVES));
3167 			scatterGroup->addChild(new GridRenderCase(m_context,
3168 													  "geometry_scatter_layers",
3169 													  "Each geometry shader instance outputs its primitives to multiple layers and far from other primitives of the same instance",
3170 													  GridRenderCase::FLAG_GEOMETRY_SCATTER_LAYERS | GridRenderCase::FLAG_GEOMETRY_SEPARATE_PRIMITIVES));
3171 		}
3172 	}
3173 
3174 	// .feedback
3175 	{
3176 		static const struct PrimitiveCaseConfig
3177 		{
3178 			const char*											name;
3179 			const char*											description;
3180 			FeedbackPrimitiveTypeCase::TessellationOutputType	tessellationOutput;
3181 			FeedbackPrimitiveTypeCase::TessellationPointMode	tessellationPointMode;
3182 			FeedbackPrimitiveTypeCase::GeometryOutputType		geometryOutputType;
3183 		} caseConfigs[] =
3184 		{
3185 			// tess output triangles -> geo input triangles, output points
3186 			{
3187 				"tessellation_output_triangles_geometry_output_points",
3188 				"Tessellation outputs triangles, geometry outputs points",
3189 				FeedbackPrimitiveTypeCase::TESSELLATION_OUT_TRIANGLES,
3190 				FeedbackPrimitiveTypeCase::TESSELLATION_POINTMODE_OFF,
3191 				FeedbackPrimitiveTypeCase::GEOMETRY_OUTPUT_POINTS
3192 			},
3193 
3194 			// tess output quads <-> geo input triangles, output points
3195 			{
3196 				"tessellation_output_quads_geometry_output_points",
3197 				"Tessellation outputs quads, geometry outputs points",
3198 				FeedbackPrimitiveTypeCase::TESSELLATION_OUT_QUADS,
3199 				FeedbackPrimitiveTypeCase::TESSELLATION_POINTMODE_OFF,
3200 				FeedbackPrimitiveTypeCase::GEOMETRY_OUTPUT_POINTS
3201 			},
3202 
3203 			// tess output isolines <-> geo input lines, output points
3204 			{
3205 				"tessellation_output_isolines_geometry_output_points",
3206 				"Tessellation outputs isolines, geometry outputs points",
3207 				FeedbackPrimitiveTypeCase::TESSELLATION_OUT_ISOLINES,
3208 				FeedbackPrimitiveTypeCase::TESSELLATION_POINTMODE_OFF,
3209 				FeedbackPrimitiveTypeCase::GEOMETRY_OUTPUT_POINTS
3210 			},
3211 
3212 			// tess output triangles, point_mode <-> geo input points, output lines
3213 			{
3214 				"tessellation_output_triangles_point_mode_geometry_output_lines",
3215 				"Tessellation outputs triangles in point mode, geometry outputs lines",
3216 				FeedbackPrimitiveTypeCase::TESSELLATION_OUT_TRIANGLES,
3217 				FeedbackPrimitiveTypeCase::TESSELLATION_POINTMODE_ON,
3218 				FeedbackPrimitiveTypeCase::GEOMETRY_OUTPUT_LINES
3219 			},
3220 
3221 			// tess output quads, point_mode <-> geo input points, output lines
3222 			{
3223 				"tessellation_output_quads_point_mode_geometry_output_lines",
3224 				"Tessellation outputs quads in point mode, geometry outputs lines",
3225 				FeedbackPrimitiveTypeCase::TESSELLATION_OUT_QUADS,
3226 				FeedbackPrimitiveTypeCase::TESSELLATION_POINTMODE_ON,
3227 				FeedbackPrimitiveTypeCase::GEOMETRY_OUTPUT_LINES
3228 			},
3229 
3230 			// tess output isolines, point_mode <-> geo input points, output triangles
3231 			{
3232 				"tessellation_output_isolines_point_mode_geometry_output_triangles",
3233 				"Tessellation outputs isolines in point mode, geometry outputs triangles",
3234 				FeedbackPrimitiveTypeCase::TESSELLATION_OUT_ISOLINES,
3235 				FeedbackPrimitiveTypeCase::TESSELLATION_POINTMODE_ON,
3236 				FeedbackPrimitiveTypeCase::GEOMETRY_OUTPUT_TRIANGLES
3237 			},
3238 		};
3239 
3240 		for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(caseConfigs); ++ndx)
3241 		{
3242 			feedbackGroup->addChild(new FeedbackPrimitiveTypeCase(m_context,
3243 																  caseConfigs[ndx].name,
3244 																  caseConfigs[ndx].description,
3245 																  caseConfigs[ndx].tessellationOutput,
3246 																  caseConfigs[ndx].tessellationPointMode,
3247 																  caseConfigs[ndx].geometryOutputType));
3248 		}
3249 
3250 		feedbackGroup->addChild(new FeedbackRecordVariableSelectionCase(m_context, "record_variable_selection", "Record a variable that has been declared as an output variable in multiple shader stages"));
3251 	}
3252 
3253 	// .point_size
3254 	{
3255 		static const struct PointSizeCaseConfig
3256 		{
3257 			const int											caseMask;
3258 			const bool											isSupportedInGL; // is this case supported in OpenGL
3259 		} caseConfigs[] =
3260 		{
3261 			{PointSizeCase::FLAG_VERTEX_SET,																									true},
3262 			{									PointSizeCase::FLAG_TESSELLATION_EVALUATION_SET,												true},
3263 			{																							PointSizeCase::FLAG_GEOMETRY_SET,		true},
3264 			{PointSizeCase::FLAG_VERTEX_SET	|	PointSizeCase::FLAG_TESSELLATION_CONTROL_SET,													false},
3265 			{PointSizeCase::FLAG_VERTEX_SET	|	PointSizeCase::FLAG_TESSELLATION_EVALUATION_SET,												true},
3266 			{PointSizeCase::FLAG_VERTEX_SET	|	PointSizeCase::FLAG_TESSELLATION_DONT_SET,														false},
3267 			{PointSizeCase::FLAG_VERTEX_SET	|															PointSizeCase::FLAG_GEOMETRY_SET,		true},
3268 			{PointSizeCase::FLAG_VERTEX_SET	|	PointSizeCase::FLAG_TESSELLATION_EVALUATION_SET		|	PointSizeCase::FLAG_GEOMETRY_SET,		true},
3269 			{PointSizeCase::FLAG_VERTEX_SET	|	PointSizeCase::FLAG_TESSELLATION_ADD				|	PointSizeCase::FLAG_GEOMETRY_ADD,		true},
3270 			{PointSizeCase::FLAG_VERTEX_SET	|	PointSizeCase::FLAG_TESSELLATION_EVALUATION_SET		|	PointSizeCase::FLAG_GEOMETRY_DONT_SET,	false},
3271 		};
3272 
3273 
3274 		for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(caseConfigs); ++ndx)
3275 		{
3276 			if (m_isGL45 && !caseConfigs[ndx].isSupportedInGL)
3277 				continue;
3278 
3279 			const std::string name = PointSizeCase::genTestCaseName(caseConfigs[ndx].caseMask);
3280 			const std::string desc = PointSizeCase::genTestCaseDescription(caseConfigs[ndx].caseMask);
3281 
3282 			pointSizeGroup->addChild(new PointSizeCase(m_context, name.c_str(), desc.c_str(), caseConfigs[ndx].caseMask));
3283 		}
3284 	}
3285 }
3286 
3287 } // Functional
3288 } // gles31
3289 } // deqp
3290