• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*-------------------------------------------------------------------------
2  * OpenGL Conformance Test Suite
3  * -----------------------------
4  *
5  * Copyright (c) 2014-2016 The Khronos Group Inc.
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
22  */ /*-------------------------------------------------------------------*/
23 
24 #include "esextcTessellationShaderQuads.hpp"
25 #include "gluContextInfo.hpp"
26 #include "gluDefs.hpp"
27 #include "glwEnums.hpp"
28 #include "glwFunctions.hpp"
29 #include "tcuTestLog.hpp"
30 #include <algorithm>
31 
32 namespace glcts
33 {
34 
35 /** Constructor
36  *
37  * @param context Test context
38  **/
TessellationShaderQuadsTests(glcts::Context & context,const ExtParameters & extParams)39 TessellationShaderQuadsTests::TessellationShaderQuadsTests(glcts::Context& context, const ExtParameters& extParams)
40 	: TestCaseGroupBase(context, extParams, "tessellation_shader_quads_tessellation",
41 						"Verifies quad tessellation functionality")
42 {
43 	/* No implementation needed */
44 }
45 
46 /**
47  * Initializes test groups for geometry shader tests
48  **/
init(void)49 void TessellationShaderQuadsTests::init(void)
50 {
51 	addChild(new glcts::TessellationShaderQuadsDegenerateCase(m_context, m_extParams));
52 	addChild(new glcts::TessellationShaderQuadsInnerTessellationLevelRounding(m_context, m_extParams));
53 }
54 
55 /** Constructor
56  *
57  * @param context Test context
58  **/
TessellationShaderQuadsDegenerateCase(Context & context,const ExtParameters & extParams)59 TessellationShaderQuadsDegenerateCase::TessellationShaderQuadsDegenerateCase(Context&			  context,
60 																			 const ExtParameters& extParams)
61 	: TestCaseBase(context, extParams, "degenerate_case",
62 				   "Verifies that only a single triangle pair covering the outer rectangle"
63 				   " is generated, if both clamped inner and outer tessellation levels are "
64 				   "set to one.")
65 	, m_vao_id(0)
66 	, m_utils(DE_NULL)
67 {
68 	/* Left blank on purpose */
69 }
70 
71 /** Deinitializes ES objects created for the test. */
deinit()72 void TessellationShaderQuadsDegenerateCase::deinit()
73 {
74 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
75 
76 	/* Call base class' deinit() */
77 	TestCaseBase::deinit();
78 
79 	/* Unbind vertex array object */
80 	gl.bindVertexArray(0);
81 
82 	/* Deinitialize utils instance */
83 	if (m_utils != DE_NULL)
84 	{
85 		delete m_utils;
86 
87 		m_utils = DE_NULL;
88 	}
89 
90 	/* Delete vertex array object */
91 	if (m_vao_id != 0)
92 	{
93 		gl.deleteVertexArrays(1, &m_vao_id);
94 
95 		m_vao_id = 0;
96 	}
97 }
98 
99 /** Initializes ES objects necessary to run the test. */
initTest()100 void TessellationShaderQuadsDegenerateCase::initTest()
101 {
102 	/* Skip if required extensions are not supported. */
103 	if (!m_is_tessellation_shader_supported)
104 	{
105 		throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED);
106 	}
107 
108 	/* Initialize Utils instance */
109 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
110 
111 	m_utils = new TessellationShaderUtils(gl, this);
112 
113 	/* Initialize vertex array object */
114 	gl.genVertexArrays(1, &m_vao_id);
115 	GLU_EXPECT_NO_ERROR(gl.getError(), "Could not generate vertex array object");
116 
117 	gl.bindVertexArray(m_vao_id);
118 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding vertex array object!");
119 
120 	/* Retrieve GL_MAX_TESS_GEN_LEVEL_EXT value */
121 	glw::GLint gl_max_tess_gen_level_value = 0;
122 
123 	gl.getIntegerv(m_glExtTokens.MAX_TESS_GEN_LEVEL, &gl_max_tess_gen_level_value);
124 	GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() call failed for GL_MAX_TESS_GEN_LEVEL_EXT pname");
125 
126 	/* Initialize all test runs */
127 	const glw::GLint   tess_levels[] = { -gl_max_tess_gen_level_value / 2, -1, 1 };
128 	const unsigned int n_tess_levels = sizeof(tess_levels) / sizeof(tess_levels[0]);
129 
130 	const _tessellation_shader_vertex_spacing vs_modes[] = {
131 		/* NOTE: We do not check "fractional even" vertex spacing since it will always
132 		 *       clamp to 2 which is out of scope for this test.
133 		 */
134 		TESSELLATION_SHADER_VERTEX_SPACING_DEFAULT,
135 		TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD
136 	};
137 	const unsigned int n_vs_modes = sizeof(vs_modes) / sizeof(vs_modes[0]);
138 
139 	/* Iterate through all vertex spacing modes */
140 	bool has_failed = false;
141 
142 	for (unsigned int n_vs_mode = 0; n_vs_mode < n_vs_modes; ++n_vs_mode)
143 	{
144 		_tessellation_shader_vertex_spacing vs_mode = vs_modes[n_vs_mode];
145 
146 		/* Iterate through all values that should be used for irrelevant tessellation levels */
147 		for (unsigned int n_tess_level = 0; n_tess_level < n_tess_levels; ++n_tess_level)
148 		{
149 			const glw::GLint tess_level = tess_levels[n_tess_level];
150 
151 			/* Set up the run descriptor.
152 			 *
153 			 * Round outer tesellation levels to 1 if necessary, since otherwise no geometry will
154 			 * be generated.
155 			 **/
156 			_run run;
157 
158 			run.inner[0]	   = (float)tess_level;
159 			run.inner[1]	   = (float)tess_level;
160 			run.outer[0]	   = (float)((tess_level < 0) ? 1 : tess_level);
161 			run.outer[1]	   = (float)((tess_level < 0) ? 1 : tess_level);
162 			run.outer[2]	   = (float)((tess_level < 0) ? 1 : tess_level);
163 			run.outer[3]	   = (float)((tess_level < 0) ? 1 : tess_level);
164 			run.vertex_spacing = vs_mode;
165 
166 			/* Retrieve vertex data for both passes */
167 			run.n_vertices = m_utils->getAmountOfVerticesGeneratedByTessellator(
168 				TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS, run.inner, run.outer, run.vertex_spacing,
169 				false); /* is_point_mode_enabled */
170 
171 			if (run.n_vertices == 0)
172 			{
173 				std::string vs_mode_string = TessellationShaderUtils::getESTokenForVertexSpacingMode(vs_mode);
174 
175 				m_testCtx.getLog() << tcu::TestLog::Message << "No vertices were generated by tessellator for: "
176 															   "inner tess levels:"
177 															   "["
178 								   << run.inner[0] << ", " << run.inner[1] << "]"
179 																			  ", outer tess levels:"
180 																			  "["
181 								   << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", "
182 								   << run.outer[3] << "]"
183 													  ", primitive mode: quads, "
184 													  "vertex spacing: "
185 								   << vs_mode_string << tcu::TestLog::EndMessage;
186 
187 				has_failed = true;
188 			}
189 			else
190 			{
191 				/* Retrieve the data buffers */
192 				run.data = m_utils->getDataGeneratedByTessellator(run.inner, false, /* is_point_mode_enabled */
193 																  TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS,
194 																  TESSELLATION_SHADER_VERTEX_ORDERING_CCW,
195 																  run.vertex_spacing, run.outer);
196 			}
197 
198 			/* Store the run data */
199 			m_runs.push_back(run);
200 		} /* for (all tessellation levels) */
201 	}	 /* for (all vertex spacing modes) */
202 
203 	if (has_failed)
204 	{
205 		TCU_FAIL("Zero vertices were generated by tessellator for at least one run which is not "
206 				 "a correct behavior");
207 	}
208 }
209 
210 /** Executes the test.
211  *
212  *  Sets the test result to QP_TEST_RESULT_FAIL if the test failed, QP_TEST_RESULT_PASS otherwise.
213  *
214  *  Note the function throws exception should an error occur!
215  *
216  *  @return STOP if the test has finished, CONTINUE to indicate iterate() should be called once again.
217  **/
iterate(void)218 tcu::TestNode::IterateResult TessellationShaderQuadsDegenerateCase::iterate(void)
219 {
220 	/* Do not execute if required extensions are not supported. */
221 	if (!m_is_tessellation_shader_supported)
222 	{
223 		throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED);
224 	}
225 
226 	/* Initialize the test */
227 	initTest();
228 
229 	/* Iterate through all runs */
230 
231 	/* The test fails if any of the runs did not generate exactly 6 coordinates */
232 	for (_runs_const_iterator run_iterator = m_runs.begin(); run_iterator != m_runs.end(); run_iterator++)
233 	{
234 		const _run& run = *run_iterator;
235 
236 		if (run.n_vertices != (2 /* triangles */ * 3 /* vertices */))
237 		{
238 			std::string vs_mode_string = TessellationShaderUtils::getESTokenForVertexSpacingMode(run.vertex_spacing);
239 
240 			m_testCtx.getLog() << tcu::TestLog::Message << "Invalid number of coordinates (" << run.n_vertices
241 							   << ", instead of 6)"
242 								  " was generated for the following tessellation configuration: "
243 								  "primitive mode:quads, "
244 								  "vertex spacing mode:"
245 							   << vs_mode_string << " inner tessellation levels:" << run.inner[0] << ", "
246 							   << run.inner[1] << " outer tessellation levels:" << run.outer[0] << ", " << run.outer[1]
247 							   << ", " << run.outer[2] << ", " << run.outer[3] << tcu::TestLog::EndMessage;
248 
249 			TCU_FAIL("Invalid number of coordinates was generated for at least one run");
250 		}
251 	} /* for (all runs) */
252 
253 	/* All runs should generate exactly the same set of triangles.
254 	 *
255 	 * Note: we must not assume any specific vertex ordering, so we cannot
256 	 *       just do a plain memcmp() here.
257 	 */
258 	const _run& base_run = *m_runs.begin();
259 
260 	for (unsigned int n_triangle = 0; n_triangle < base_run.n_vertices / 3 /* vertices per triangle */; n_triangle++)
261 	{
262 		const float* base_triangle_data = (const float*)(&base_run.data[0]) +
263 										  3		  /* vertices per triangle */
264 											  * 3 /* components */
265 											  * n_triangle;
266 
267 		for (_runs_const_iterator ref_run_iterator = m_runs.begin() + 1; ref_run_iterator != m_runs.end();
268 			 ref_run_iterator++)
269 		{
270 			const _run& ref_run = *ref_run_iterator;
271 
272 			const float* ref_triangle_data1 = (const float*)(&ref_run.data[0]);
273 			const float* ref_triangle_data2 =
274 				(const float*)(&ref_run.data[0]) + 3 /* vertices per triangle */ * 3 /* components */;
275 
276 			if (!TessellationShaderUtils::isTriangleDefined(base_triangle_data, ref_triangle_data1) &&
277 				!TessellationShaderUtils::isTriangleDefined(base_triangle_data, ref_triangle_data2))
278 			{
279 				std::string base_vs_mode_string =
280 					TessellationShaderUtils::getESTokenForVertexSpacingMode(base_run.vertex_spacing);
281 				std::string ref_vs_mode_string =
282 					TessellationShaderUtils::getESTokenForVertexSpacingMode(ref_run.vertex_spacing);
283 
284 				m_testCtx.getLog() << tcu::TestLog::Message
285 								   << "Reference run does not contain a triangle found in a base run"
286 									  " generated for the following tessellation configuration: "
287 									  "primitive mode:quads, "
288 									  "base vertex spacing mode:"
289 								   << base_vs_mode_string << " base inner tessellation levels:" << base_run.inner[0]
290 								   << ", " << base_run.inner[1]
291 								   << " base outer tessellation levels:" << base_run.outer[0] << ", "
292 								   << base_run.outer[1] << ", " << base_run.outer[2] << ", " << base_run.outer[3]
293 								   << "reference vertex spacing mode:" << ref_vs_mode_string
294 								   << " reference inner tessellation levels:" << ref_run.inner[0] << ", "
295 								   << ref_run.inner[1] << " reference outer tessellation levels:" << ref_run.outer[0]
296 								   << ", " << ref_run.outer[1] << ", " << ref_run.outer[2] << ", " << ref_run.outer[3]
297 								   << tcu::TestLog::EndMessage;
298 
299 				TCU_FAIL("Reference run does not contain a triangle found in a base run");
300 			}
301 		} /* for (all reference runs) */
302 	}	 /* for (all triangles) */
303 
304 	/* All done */
305 	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
306 	return STOP;
307 }
308 
309 /** Constructor
310  *
311  * @param context Test context
312  **/
TessellationShaderQuadsInnerTessellationLevelRounding(Context & context,const ExtParameters & extParams)313 TessellationShaderQuadsInnerTessellationLevelRounding::TessellationShaderQuadsInnerTessellationLevelRounding(
314 	Context& context, const ExtParameters& extParams)
315 	: TestCaseBase(context, extParams, "inner_tessellation_level_rounding",
316 				   "Verifies that either inner tessellation level is rounded to 2 or 3,"
317 				   " when the tessellator is run in quads primitive mode and "
318 				   "corresponding inner tessellation level is set to 1.")
319 	, m_vao_id(0)
320 	, m_utils(DE_NULL)
321 {
322 	/* Left blank on purpose */
323 }
324 
325 /** Deinitializes ES objects created for the test. */
deinit()326 void TessellationShaderQuadsInnerTessellationLevelRounding::deinit()
327 {
328 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
329 
330 	/* Call base class' deinit() */
331 	TestCaseBase::deinit();
332 
333 	/* Unbind vertex array object */
334 	gl.bindVertexArray(0);
335 
336 	/* Deinitialize utils instance */
337 	if (m_utils != DE_NULL)
338 	{
339 		delete m_utils;
340 
341 		m_utils = DE_NULL;
342 	}
343 
344 	/* Delete vertex array object */
345 	if (m_vao_id != 0)
346 	{
347 		gl.deleteVertexArrays(1, &m_vao_id);
348 
349 		m_vao_id = 0;
350 	}
351 }
352 
353 /** Takes a vertex data set and returns a vector of unique vec2s found in the set.
354  *
355  *  @param raw_data            Vertex data set to process.
356  *  @param n_raw_data_vertices Amount of 3-component vertices found under @param raw_data.
357  *
358  *  @return As per description.
359  **/
getUniqueTessCoordinatesFromVertexDataSet(const float * raw_data,const unsigned int n_raw_data_vertices)360 std::vector<_vec2> TessellationShaderQuadsInnerTessellationLevelRounding::getUniqueTessCoordinatesFromVertexDataSet(
361 	const float* raw_data, const unsigned int n_raw_data_vertices)
362 {
363 	std::vector<_vec2> result;
364 
365 	for (unsigned int n_vertex = 0; n_vertex < n_raw_data_vertices; n_vertex += 2)
366 	{
367 		const float* vertex_data = raw_data + n_vertex * 3 /* components */;
368 		_vec2		 vertex(vertex_data[0], vertex_data[1]);
369 
370 		if (std::find(result.begin(), result.end(), vertex) == result.end())
371 		{
372 			result.push_back(vertex);
373 		}
374 
375 	} /* for (all vertices) */
376 
377 	return result;
378 }
379 
380 /** Initializes ES objects necessary to run the test. */
initTest()381 void TessellationShaderQuadsInnerTessellationLevelRounding::initTest()
382 {
383 	/* Skip if required extensions are not supported. */
384 	if (!m_is_tessellation_shader_supported)
385 	{
386 		throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED);
387 	}
388 
389 	/* Initialize Utils instance */
390 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
391 
392 	m_utils = new TessellationShaderUtils(gl, this);
393 
394 	/* Initialize vertex array object */
395 	gl.genVertexArrays(1, &m_vao_id);
396 	GLU_EXPECT_NO_ERROR(gl.getError(), "Could not generate vertex array object");
397 
398 	gl.bindVertexArray(m_vao_id);
399 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding vertex array object!");
400 
401 	/* Retrieve GL_MAX_TESS_GEN_LEVEL_EXT value */
402 	glw::GLint gl_max_tess_gen_level_value = 0;
403 
404 	gl.getIntegerv(m_glExtTokens.MAX_TESS_GEN_LEVEL, &gl_max_tess_gen_level_value);
405 	GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() call failed for GL_MAX_TESS_GEN_LEVEL_EXT pname");
406 
407 	/* Initialize all test runs */
408 	const glw::GLint tess_levels[] = { 2, gl_max_tess_gen_level_value / 2, gl_max_tess_gen_level_value };
409 
410 	const glw::GLint tess_levels_odd[] = { 2 + 1, gl_max_tess_gen_level_value / 2 + 1,
411 										   gl_max_tess_gen_level_value - 1 };
412 	const unsigned int n_tess_levels = sizeof(tess_levels) / sizeof(tess_levels[0]);
413 
414 	const _tessellation_shader_vertex_spacing vs_modes[] = { TESSELLATION_SHADER_VERTEX_SPACING_EQUAL,
415 															 TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_EVEN,
416 															 TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD };
417 	const unsigned int n_vs_modes = sizeof(vs_modes) / sizeof(vs_modes[0]);
418 
419 	/* Iterate through all vertex spacing modes */
420 	for (unsigned int n_vs_mode = 0; n_vs_mode < n_vs_modes; ++n_vs_mode)
421 	{
422 		_tessellation_shader_vertex_spacing vs_mode = vs_modes[n_vs_mode];
423 
424 		/* Iterate through all values that should be used for irrelevant tessellation levels */
425 		for (unsigned int n_tess_level = 0; n_tess_level < n_tess_levels; ++n_tess_level)
426 		{
427 			/* We need to test two cases in this test:
428 			 *
429 			 * a) inner[0] is set to 1 for set A and, for set B, to the value we're expecting the level to
430 			 *    round, given the vertex spacing mode. inner[1] is irrelevant.
431 			 * b) inner[0] is irrelevant. inner[1] is set to 1 for set A and, for set B, to the value we're
432 			 *    expecting the level to round, given the vertex spacing mode.
433 			 */
434 			for (unsigned int n_inner_tess_level_combination = 0;
435 				 n_inner_tess_level_combination < 2; /* inner[0], inner[1] */
436 				 ++n_inner_tess_level_combination)
437 			{
438 				/* Set up the run descriptor */
439 				glw::GLint tess_level = (vs_mode == TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD) ?
440 											tess_levels_odd[n_tess_level] :
441 											tess_levels[n_tess_level];
442 				_run run;
443 
444 				/* Determine inner tessellation level values for two cases */
445 				switch (n_inner_tess_level_combination)
446 				{
447 				case 0:
448 				{
449 					run.set1_inner[0] = 1.0f;
450 					run.set1_inner[1] = (glw::GLfloat)tess_level;
451 					run.set2_inner[1] = (glw::GLfloat)tess_level;
452 
453 					TessellationShaderUtils::getTessellationLevelAfterVertexSpacing(
454 						vs_mode, run.set1_inner[0] + 1.0f /* epsilon */, gl_max_tess_gen_level_value,
455 						DE_NULL, /* out_clamped */
456 						run.set2_inner);
457 					break;
458 				} /* case 0: */
459 
460 				case 1:
461 				{
462 					run.set1_inner[0] = (glw::GLfloat)tess_level;
463 					run.set1_inner[1] = 1.0f;
464 					run.set2_inner[0] = (glw::GLfloat)tess_level;
465 
466 					TessellationShaderUtils::getTessellationLevelAfterVertexSpacing(
467 						vs_mode, run.set1_inner[1] + 1.0f /* epsilon */, gl_max_tess_gen_level_value,
468 						DE_NULL, /* out_clamped */
469 						run.set2_inner + 1);
470 					break;
471 				} /* case 1: */
472 
473 				default:
474 					TCU_FAIL("Invalid inner tessellation level combination index");
475 				} /* switch (n_inner_tess_level_combination) */
476 
477 				/* Configure outer tessellation level values */
478 				run.set1_outer[0] = (glw::GLfloat)tess_level;
479 				run.set2_outer[0] = (glw::GLfloat)tess_level;
480 				run.set1_outer[1] = (glw::GLfloat)tess_level;
481 				run.set2_outer[1] = (glw::GLfloat)tess_level;
482 				run.set1_outer[2] = (glw::GLfloat)tess_level;
483 				run.set2_outer[2] = (glw::GLfloat)tess_level;
484 				run.set1_outer[3] = (glw::GLfloat)tess_level;
485 				run.set2_outer[3] = (glw::GLfloat)tess_level;
486 
487 				/* Set up remaining run properties */
488 				run.vertex_spacing = vs_mode;
489 
490 				/* Retrieve vertex data for both passes */
491 				glw::GLint n_set1_vertices = 0;
492 				glw::GLint n_set2_vertices = 0;
493 
494 				n_set1_vertices = m_utils->getAmountOfVerticesGeneratedByTessellator(
495 					TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS, run.set1_inner, run.set1_outer, run.vertex_spacing,
496 					false); /* is_point_mode_enabled */
497 				n_set2_vertices = m_utils->getAmountOfVerticesGeneratedByTessellator(
498 					TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS, run.set2_inner, run.set2_outer, run.vertex_spacing,
499 					false); /* is_point_mode_enabled */
500 
501 				if (n_set1_vertices == 0)
502 				{
503 					std::string vs_mode_string = TessellationShaderUtils::getESTokenForVertexSpacingMode(vs_mode);
504 
505 					m_testCtx.getLog() << tcu::TestLog::Message << "No vertices were generated by tessellator for: "
506 																   "inner tess levels:"
507 																   "["
508 									   << run.set1_inner[0] << ", " << run.set1_inner[1] << "]"
509 																							", outer tess levels:"
510 																							"["
511 									   << run.set1_outer[0] << ", " << run.set1_outer[1] << ", " << run.set1_outer[2]
512 									   << ", " << run.set1_outer[3] << "]"
513 																	   ", primitive mode: quads, "
514 																	   "vertex spacing: "
515 									   << vs_mode_string << tcu::TestLog::EndMessage;
516 
517 					TCU_FAIL("Zero vertices were generated by tessellator for first test pass");
518 				}
519 
520 				if (n_set2_vertices == 0)
521 				{
522 					std::string vs_mode_string = TessellationShaderUtils::getESTokenForVertexSpacingMode(vs_mode);
523 
524 					m_testCtx.getLog() << tcu::TestLog::Message << "No vertices were generated by tessellator for: "
525 																   "inner tess levels:"
526 																   "["
527 									   << run.set2_inner[0] << ", " << run.set2_inner[1] << "]"
528 																							", outer tess levels:"
529 																							"["
530 									   << run.set2_outer[0] << ", " << run.set2_outer[1] << ", " << run.set2_outer[2]
531 									   << ", " << run.set2_outer[3] << "]"
532 																	   ", primitive mode: quads, "
533 																	   "vertex spacing: "
534 									   << vs_mode_string << tcu::TestLog::EndMessage;
535 
536 					TCU_FAIL("Zero vertices were generated by tessellator for second test pass");
537 				}
538 
539 				if (n_set1_vertices != n_set2_vertices)
540 				{
541 					std::string vs_mode_string = TessellationShaderUtils::getESTokenForVertexSpacingMode(vs_mode);
542 
543 					m_testCtx.getLog() << tcu::TestLog::Message
544 									   << "Amount of vertices generated by the tessellator differs"
545 										  " for the following inner/outer configs: "
546 										  "inner tess levels:"
547 										  "["
548 									   << run.set1_inner[0] << ", " << run.set1_inner[1] << "]"
549 																							", outer tess levels:"
550 																							"["
551 									   << run.set1_outer[0] << ", " << run.set1_outer[1] << ", " << run.set1_outer[2]
552 									   << ", " << run.set1_outer[3] << "]"
553 																	   " and inner tess levels:"
554 																	   "["
555 									   << run.set2_inner[0] << ", " << run.set2_inner[1] << "]"
556 																							", outer tess levels:"
557 																							"["
558 									   << run.set2_outer[0] << ", " << run.set2_outer[1] << ", " << run.set2_outer[2]
559 									   << ", " << run.set2_outer[3] << "]"
560 																	   ", primitive mode: quads, vertex spacing: "
561 									   << vs_mode_string << tcu::TestLog::EndMessage;
562 
563 					TCU_FAIL("Amount of vertices generated by tessellator differs between base and references passes");
564 				}
565 
566 				/* Store the amount of vertices in run properties */
567 				run.n_vertices = n_set1_vertices;
568 
569 				/* Retrieve the data buffers */
570 				run.set1_data = m_utils->getDataGeneratedByTessellator(
571 					run.set1_inner, false, /* is_point_mode_enabled */
572 					TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS, TESSELLATION_SHADER_VERTEX_ORDERING_CCW,
573 					run.vertex_spacing, run.set1_outer);
574 				run.set2_data = m_utils->getDataGeneratedByTessellator(
575 					run.set2_inner, false, /* is_point_mode_enabled */
576 					TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS, TESSELLATION_SHADER_VERTEX_ORDERING_CCW,
577 					run.vertex_spacing, run.set2_outer);
578 
579 				/* Store the run data */
580 				m_runs.push_back(run);
581 			} /* for (all inner tess level combinations) */
582 		}	 /* for (all sets) */
583 	}		  /* for (all vertex spacing modes) */
584 }
585 
586 /** Executes the test.
587  *
588  *  Sets the test result to QP_TEST_RESULT_FAIL if the test failed, QP_TEST_RESULT_PASS otherwise.
589  *
590  *  Note the function throws exception should an error occur!
591  *
592  *  @return STOP if the test has finished, CONTINUE to indicate iterate() should be called once again.
593  **/
iterate(void)594 tcu::TestNode::IterateResult TessellationShaderQuadsInnerTessellationLevelRounding::iterate(void)
595 {
596 	/* Do not execute if required extensions are not supported. */
597 	if (!m_is_tessellation_shader_supported)
598 	{
599 		throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED);
600 	}
601 
602 	/* Initialize the test */
603 	initTest();
604 
605 	/* Iterate through all runs */
606 
607 	for (_runs_const_iterator run_iterator = m_runs.begin(); run_iterator != m_runs.end(); run_iterator++)
608 	{
609 		const _run& run = *run_iterator;
610 
611 		if (run.vertex_spacing != TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD)
612 		{
613 			/* Make sure the vertex data generated for two passes matches */
614 			const glw::GLint n_triangles = run.n_vertices / 3 /* vertices per triangle */;
615 
616 			std::vector<bool> triangleMatched;
617 			triangleMatched.assign(n_triangles, false);
618 
619 			for (int n_triangle = 0; n_triangle < n_triangles; ++n_triangle)
620 			{
621 				const float* triangle_a = (const float*)(&run.set1_data[0]) +
622 										  n_triangle * 3 /* vertices */
623 											  * 3;		 /* components */
624 				/* Look up matching triangle */
625 				bool triangleFound = false;
626 
627 				for (int n_triangle_b = 0; n_triangle_b < n_triangles; ++n_triangle_b)
628 				{
629 					if (!triangleMatched[n_triangle_b])
630 					{
631 						const float* triangle_b = (const float*)(&run.set2_data[0]) +
632 												  n_triangle_b * 3 /* vertices */
633 													  * 3;		   /* components */
634 
635 						if (TessellationShaderUtils::isTriangleDefined(triangle_a, triangle_b))
636 						{
637 							triangleMatched[n_triangle_b] = true;
638 							triangleFound				  = true;
639 							break;
640 						}
641 					}
642 				}
643 
644 				if (!triangleFound)
645 				{
646 					std::string vs_mode_string =
647 						TessellationShaderUtils::getESTokenForVertexSpacingMode(run.vertex_spacing);
648 
649 					m_testCtx.getLog() << tcu::TestLog::Message
650 									   << "The following triangle, generated in the first pass, was not "
651 										  "generated in the second one. "
652 										  "First pass' configuration: inner tess levels:"
653 										  "["
654 									   << run.set1_inner[0] << ", " << run.set1_inner[1] << "]"
655 																							", outer tess levels:"
656 																							"["
657 									   << run.set1_outer[0] << ", " << run.set1_outer[1] << ", " << run.set1_outer[2]
658 									   << ", " << run.set1_outer[3]
659 									   << "]"
660 										  "; second pass' configuration: inner tess levels:"
661 										  "["
662 									   << run.set2_inner[0] << ", " << run.set2_inner[1] << "]"
663 																							", outer tess levels:"
664 																							"["
665 									   << run.set2_outer[0] << ", " << run.set2_outer[1] << ", " << run.set2_outer[2]
666 									   << ", " << run.set2_outer[3] << "]"
667 																	   ", primitive mode: quads, vertex spacing: "
668 									   << vs_mode_string << tcu::TestLog::EndMessage;
669 
670 					TCU_FAIL("A triangle from first pass' data set was not found in second pass' data set.");
671 				}
672 			} /* for (all vertices) */
673 		}	 /* if (run.vertex_spacing != TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD) */
674 		else
675 		{
676 			/* For Fractional Odd vertex spacing, we cannot apply the above methodology because of the fact
677 			 * two of the inner edges (the ones subdivided with tessellation level of 1.0) will have been
678 			 * subdivided differently between two runs, causing the vertex positions to shift, ultimately
679 			 * leading to isTriangleDefined() failure.
680 			 *
681 			 * Hence, we need to take a bit different approach for this case. If you look at a visualization
682 			 * of a tessellated quad, for which one of the inner tess levels has been set to 1 (let's assume
683 			 * inner[0] for the sake of discussion), and then looked at a list of points that were generated by the
684 			 * tessellator, you'll notice that even though it looks like we only have one span of triangles
685 			 * horizontally, there are actually three. The two outermost spans on each side (the "outer
686 			 * tessellation regions") are actually degenerate, because they have epsilon spacing allocated to
687 			 * them.
688 			 *
689 			 * Using the theory above, we look for a so-called "marker triangle". In the inner[0] = 1.0 case,
690 			 * it's one of the two triangles, one edge of which spans horizontally across the whole domain, and
691 			 * the other two edges touch the outermost edge of the quad. In the inner[1] = 1.0 case, Xs are flipped
692 			 * with Ys, but the general idea stays the same.
693 			 *
694 			 * So for fractional odd vertex spacing, this test verifies that the two marker triangles capping the
695 			 * opposite ends of the inner quad tessellation region exist.
696 			 */
697 
698 			/* Convert arrayed triangle-based representation to a vector storing unique tessellation
699 			 * coordinates.
700 			 */
701 			std::vector<_vec2> set1_tess_coordinates =
702 				getUniqueTessCoordinatesFromVertexDataSet((const float*)(&run.set1_data[0]), run.n_vertices);
703 
704 			/* Extract and sort the coordinate components we have from
705 			 * the minimum to the maximum */
706 			std::vector<float> set1_tess_coordinates_x_sorted;
707 			std::vector<float> set1_tess_coordinates_y_sorted;
708 
709 			for (std::vector<_vec2>::iterator coordinate_iterator = set1_tess_coordinates.begin();
710 				 coordinate_iterator != set1_tess_coordinates.end(); coordinate_iterator++)
711 			{
712 				const _vec2& coordinate = *coordinate_iterator;
713 
714 				if (std::find(set1_tess_coordinates_x_sorted.begin(), set1_tess_coordinates_x_sorted.end(),
715 							  coordinate.x) == set1_tess_coordinates_x_sorted.end())
716 				{
717 					set1_tess_coordinates_x_sorted.push_back(coordinate.x);
718 				}
719 
720 				if (std::find(set1_tess_coordinates_y_sorted.begin(), set1_tess_coordinates_y_sorted.end(),
721 							  coordinate.y) == set1_tess_coordinates_y_sorted.end())
722 				{
723 					set1_tess_coordinates_y_sorted.push_back(coordinate.y);
724 				}
725 			} /* for (all tessellation coordinates) */
726 
727 			std::sort(set1_tess_coordinates_x_sorted.begin(), set1_tess_coordinates_x_sorted.end());
728 			std::sort(set1_tess_coordinates_y_sorted.begin(), set1_tess_coordinates_y_sorted.end());
729 
730 			/* Sanity checks */
731 			DE_ASSERT(set1_tess_coordinates_x_sorted.size() > 2);
732 			DE_ASSERT(set1_tess_coordinates_y_sorted.size() > 2);
733 
734 			/* NOTE: This code could have been merged for both cases at the expense of code readability. */
735 			if (run.set1_inner[0] == 1.0f)
736 			{
737 				/* Look for the second horizontal line segment, starting from top and bottom */
738 				const float second_y_from_top = set1_tess_coordinates_y_sorted[1];
739 				const float second_y_from_bottom =
740 					set1_tess_coordinates_y_sorted[set1_tess_coordinates_y_sorted.size() - 2];
741 
742 				/* In this particular case, there should be exactly one triangle spanning
743 				 * from U=0 to U=1 at both these heights, with the third coordinate located
744 				 * at the "outer" edge.
745 				 */
746 				for (int n = 0; n < 2 /* cases */; ++n)
747 				{
748 					float y1_y2 = 0.0f;
749 					float y3	= 0.0f;
750 
751 					if (n == 0)
752 					{
753 						y1_y2 = second_y_from_bottom;
754 						y3	= 1.0f;
755 					}
756 					else
757 					{
758 						y1_y2 = second_y_from_top;
759 						y3	= 0.0f;
760 					}
761 
762 					/* Try to find the triangle */
763 					bool has_found_triangle = false;
764 
765 					DE_ASSERT((run.n_vertices % 3) == 0);
766 
767 					for (unsigned int n_triangle = 0; n_triangle < run.n_vertices / 3; ++n_triangle)
768 					{
769 						const float* vertex1_data = (const float*)(&run.set1_data[0]) +
770 													3		/* vertices */
771 														* 3 /* components */
772 														* n_triangle;
773 						const float* vertex2_data = vertex1_data + 3 /* components */;
774 						const float* vertex3_data = vertex2_data + 3 /* components */;
775 
776 						/* Make sure at least two Y coordinates are equal to y1_y2. */
777 						const float* y1_vertex_data = DE_NULL;
778 						const float* y2_vertex_data = DE_NULL;
779 						const float* y3_vertex_data = DE_NULL;
780 
781 						if (vertex1_data[1] == y1_y2)
782 						{
783 							if (vertex2_data[1] == y1_y2 && vertex3_data[1] == y3)
784 							{
785 								y1_vertex_data = vertex1_data;
786 								y2_vertex_data = vertex2_data;
787 								y3_vertex_data = vertex3_data;
788 							}
789 							else if (vertex2_data[1] == y3 && vertex3_data[1] == y1_y2)
790 							{
791 								y1_vertex_data = vertex1_data;
792 								y2_vertex_data = vertex3_data;
793 								y3_vertex_data = vertex2_data;
794 							}
795 						}
796 						else if (vertex2_data[1] == y1_y2 && vertex3_data[1] == y1_y2 && vertex1_data[1] == y3)
797 						{
798 							y1_vertex_data = vertex2_data;
799 							y2_vertex_data = vertex3_data;
800 							y3_vertex_data = vertex1_data;
801 						}
802 
803 						if (y1_vertex_data != DE_NULL && y2_vertex_data != DE_NULL && y3_vertex_data != DE_NULL)
804 						{
805 							/* Vertex 1 and 2 should span across whole domain horizontally */
806 							if ((y1_vertex_data[0] == 0.0f && y2_vertex_data[0] == 1.0f) ||
807 								(y1_vertex_data[0] == 1.0f && y2_vertex_data[0] == 0.0f))
808 							{
809 								has_found_triangle = true;
810 
811 								break;
812 							}
813 						}
814 					} /* for (all triangles) */
815 
816 					if (!has_found_triangle)
817 					{
818 						TCU_FAIL("Could not find a marker triangle");
819 					}
820 				} /* for (both cases) */
821 			}	 /* if (run.set1_inner[0] == 1.0f) */
822 			else
823 			{
824 				DE_ASSERT(run.set1_inner[1] == 1.0f);
825 
826 				/* Look for the second vertical line segment, starting from left and right */
827 				const float second_x_from_left = set1_tess_coordinates_x_sorted[1];
828 				const float second_x_from_right =
829 					set1_tess_coordinates_x_sorted[set1_tess_coordinates_x_sorted.size() - 2];
830 
831 				/* In this particular case, there should be exactly one triangle spanning
832 				 * from V=0 to V=1 at both these widths, with the third coordinate located
833 				 * at the "outer" edge.
834 				 */
835 				for (int n = 0; n < 2 /* cases */; ++n)
836 				{
837 					float x1_x2 = 0.0f;
838 					float x3	= 0.0f;
839 
840 					if (n == 0)
841 					{
842 						x1_x2 = second_x_from_right;
843 						x3	= 1.0f;
844 					}
845 					else
846 					{
847 						x1_x2 = second_x_from_left;
848 						x3	= 0.0f;
849 					}
850 
851 					/* Try to find the triangle */
852 					bool has_found_triangle = false;
853 
854 					DE_ASSERT((run.n_vertices % 3) == 0);
855 
856 					for (unsigned int n_triangle = 0; n_triangle < run.n_vertices / 3; ++n_triangle)
857 					{
858 						const float* vertex1_data =
859 							(const float*)(&run.set1_data[0]) + 3 /* vertices */ * 3 /* components */ * n_triangle;
860 						const float* vertex2_data = vertex1_data + 3 /* components */;
861 						const float* vertex3_data = vertex2_data + 3 /* components */;
862 
863 						/* Make sure at least two X coordinates are equal to x1_x2. */
864 						const float* x1_vertex_data = DE_NULL;
865 						const float* x2_vertex_data = DE_NULL;
866 						const float* x3_vertex_data = DE_NULL;
867 
868 						if (vertex1_data[0] == x1_x2)
869 						{
870 							if (vertex2_data[0] == x1_x2 && vertex3_data[0] == x3)
871 							{
872 								x1_vertex_data = vertex1_data;
873 								x2_vertex_data = vertex2_data;
874 								x3_vertex_data = vertex3_data;
875 							}
876 							else if (vertex2_data[0] == x3 && vertex3_data[0] == x1_x2)
877 							{
878 								x1_vertex_data = vertex1_data;
879 								x2_vertex_data = vertex3_data;
880 								x3_vertex_data = vertex2_data;
881 							}
882 						}
883 						else if (vertex2_data[0] == x1_x2 && vertex3_data[0] == x1_x2 && vertex1_data[0] == x3)
884 						{
885 							x1_vertex_data = vertex2_data;
886 							x2_vertex_data = vertex3_data;
887 							x3_vertex_data = vertex1_data;
888 						}
889 
890 						if (x1_vertex_data != DE_NULL && x2_vertex_data != DE_NULL && x3_vertex_data != DE_NULL)
891 						{
892 							/* Vertex 1 and 2 should span across whole domain vertically */
893 							if ((x1_vertex_data[1] == 0.0f && x2_vertex_data[1] == 1.0f) ||
894 								(x1_vertex_data[1] == 1.0f && x2_vertex_data[1] == 0.0f))
895 							{
896 								has_found_triangle = true;
897 
898 								break;
899 							}
900 						}
901 					} /* for (all triangles) */
902 
903 					if (!has_found_triangle)
904 					{
905 						TCU_FAIL("Could not find a marker triangle (implies invalid quad tessellation for the case "
906 								 "considered)");
907 					}
908 				} /* for (both cases) */
909 			}
910 		}
911 	} /* for (all runs) */
912 
913 	/* All done */
914 	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
915 	return STOP;
916 }
917 
918 } /* namespace glcts */
919