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 "esextcTessellationShaderVertexSpacing.hpp"
25#include "esextcTessellationShaderUtils.hpp"
26#include "gluContextInfo.hpp"
27#include "gluDefs.hpp"
28#include "glwEnums.hpp"
29#include "glwFunctions.hpp"
30#include "tcuTestLog.hpp"
31#include <algorithm>
32
33/* Precision, with which the test should be executed. */
34const float epsilon = 1e-3f;
35
36namespace glcts
37{
38/** Compares two barycentric/cartesian coordinates, using test-wide epsilon.
39 *
40 *  @param in Coordinate to compare current instance against.
41 *
42 *  @return true if the coordinates are equal, false otherwise.
43 **/
44bool TessellationShaderVertexSpacing::_tess_coordinate::operator==(
45	const TessellationShaderVertexSpacing::_tess_coordinate& in) const
46{
47	if (de::abs(this->u - in.u) < epsilon && de::abs(this->v - in.v) < epsilon && de::abs(this->w - in.w) < epsilon)
48	{
49		return true;
50	}
51	else
52	{
53		return false;
54	}
55}
56
57/** Compares two Cartesian coordinates, using test-wide epsilon.
58 *
59 *  @param in Coordinate to compare current instance against.
60 *
61 *  @return true if the coordinates are equal, false otherwise.
62 **/
63bool TessellationShaderVertexSpacing::_tess_coordinate_cartesian::operator==(
64	const TessellationShaderVertexSpacing::_tess_coordinate_cartesian& in) const
65{
66	if (de::abs(this->x - in.x) < epsilon && de::abs(this->y - in.y) < epsilon)
67	{
68		return true;
69	}
70	else
71	{
72		return false;
73	}
74}
75
76/** Constructor
77 *
78 * @param context Test context
79 **/
80TessellationShaderVertexSpacing::TessellationShaderVertexSpacing(Context& context, const ExtParameters& extParams)
81	: TestCaseBase(context, extParams, "vertex_spacing", "Verifies vertex spacing qualifier behaves as specified")
82	, m_gl_max_tess_gen_level_value(0)
83	, m_vao_id(0)
84	, m_utils(DE_NULL)
85{
86	/* Left blank on purpose */
87}
88
89/** Comparator function, used to compare two _tess_coordinate_cartesian
90 *  instances by their X components.
91 *
92 *  @param a First coordinate to use for comparison;
93 *  @param b Second coordinate to use for comparison.
94 *
95 *  @return true  if X component of @param a is lower than b's;
96 *          false otherwise.
97 **/
98bool TessellationShaderVertexSpacing::compareEdgeByX(_tess_coordinate_cartesian a, _tess_coordinate_cartesian b)
99{
100	return a.x < b.x;
101}
102
103/** Comparator function, used to compare two _tess_coordinate_cartesian
104 *  instances by their Y components.
105 *
106 *  @param a First coordinate to use for comparison;
107 *  @param b Second coordinate to use for comparison.
108 *
109 *  @return true  if Y component of @param a is lower than b's;
110 *          false otherwise.
111 **/
112bool TessellationShaderVertexSpacing::compareEdgeByY(_tess_coordinate_cartesian a, _tess_coordinate_cartesian b)
113{
114	return a.y < b.y;
115}
116
117/** Deinitializes ES objects created for the test. */
118void TessellationShaderVertexSpacing::deinit()
119{
120	/* Call base class' deinit() */
121	TestCaseBase::deinit();
122
123	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
124
125	/* Unbind vertex array object */
126	gl.bindVertexArray(0);
127
128	/* Delete vertex array object */
129	if (m_vao_id != 0)
130	{
131		gl.deleteVertexArrays(1, &m_vao_id);
132
133		m_vao_id = 0;
134	}
135
136	/* Deinitialize utils instance */
137	if (m_utils != DE_NULL)
138	{
139		delete m_utils;
140
141		m_utils = DE_NULL;
142	}
143}
144
145/**  Takes data generated by tessellator for a specific run configuration and
146 *  converts it into a set of edges that correspond to subsequent isolines.
147 *  This function asserts that the run uses a 'isolines' primitive mode.
148 *
149 *  @param run Test run properties.
150 *
151 *  @return A vector storing found edges.
152 **/
153TessellationShaderVertexSpacing::_tess_edges TessellationShaderVertexSpacing::getEdgesForIsolinesTessellation(
154	const _run& run)
155{
156	_tess_edges result;
157
158	/* First convert the array data to a vector of edges, where each edge
159	 * is a vector of points with the same V component. After this is done,
160	 * points for each edge need to be sorted by U component.
161	 */
162	for (unsigned int n_vertex = 0; n_vertex < run.n_vertices; ++n_vertex)
163	{
164		/* Isolines are simple - we only need to create a new edge per each unique height */
165		const float*			   coordinate = (const float*)(&run.data[0]) + 3 /* components */ * n_vertex;
166		_tess_coordinate_cartesian new_item;
167
168		new_item.x = coordinate[0];
169		new_item.y = coordinate[1];
170
171		/* Is V recognized? */
172		_tess_edges_iterator edges_iterator;
173
174		for (edges_iterator = result.begin(); edges_iterator != result.end(); edges_iterator++)
175		{
176			_tess_edge& edge = *edges_iterator;
177
178			/* Each edge uses the same Y component, so we only need to check the first entry */
179			if (de::abs(edge.points[0].y - coordinate[1]) < epsilon)
180			{
181				/* Add the new point to the vector */
182				edge.points.push_back(new_item);
183
184				break;
185			}
186		} /* for (all edges) */
187
188		if (edges_iterator == result.end())
189		{
190			/* New edge starts at this point.
191			 *
192			 * Note that outermost tessellation level does not apply to this
193			 * primitive mode.
194			 **/
195			_tess_edge new_edge(run.outer[1], run.outer[1], 1.0f);
196
197			new_edge.points.push_back(new_item);
198
199			result.push_back(new_edge);
200		}
201	} /* for (all vertices) */
202
203	/* For each edge, sort the points by U coordinate */
204	for (_tess_edges_iterator edges_iterator = result.begin(); edges_iterator != result.end(); ++edges_iterator)
205	{
206		_tess_edge_points& edge_points = edges_iterator->points;
207
208		std::sort(edge_points.begin(), edge_points.end(), compareEdgeByX);
209	}
210
211	/* Done */
212	return result;
213}
214
215/**  Takes data generated by tessellator for a specific run configuration and
216 *  converts it into a set of edges that define the outer and inner quad.
217 *  This function asserts that the run uses a 'quads' primitive mode.
218 *
219 *  @param run Test run properties.
220 *
221 *  @return A vector storing found edges.
222 **/
223TessellationShaderVertexSpacing::_tess_edges TessellationShaderVertexSpacing::getEdgesForQuadsTessellation(
224	const _run& run)
225{
226	_tess_edges result;
227
228	/* First, convert the raw coordinate array into a vector of cartesian coordinates.
229	 * For this method, we will need to take out vertices in no specific order. */
230	std::vector<_tess_coordinate_cartesian> coordinates;
231
232	for (unsigned int n_vertex = 0; n_vertex < run.n_vertices; ++n_vertex)
233	{
234		_tess_coordinate_cartesian new_coordinate;
235		const float*			   vertex_data = (const float*)(&run.data[0]) + 3 /* components */ * n_vertex;
236
237		new_coordinate.x = vertex_data[0];
238		new_coordinate.y = vertex_data[1];
239
240		coordinates.push_back(new_coordinate);
241	}
242
243	/* Data set is expected to describe an outer and inner rectangles. We will execute the quad extraction
244	 * process in two iterations:
245	 *
246	 * - first iteration will determine all coordinates that are part of the outer quad;
247	 * - second iteration will focus on the inner quad.
248	 *
249	 * Each iteration will start from identifying corner vertices:
250	 *
251	 * - top-left corner     (x delta from (0.5, 0.5) should be negative, y delta from (0.5, 0.5) should be positive);
252	 * - top-right corner    (x delta from (0.5, 0.5) should be positive, y delta from (0.5, 0.5) should be positive);
253	 * - bottom-left corner  (x delta from (0.5, 0.5) should be negative, y delta from (0.5, 0.5) should be negative);
254	 * - bottom-right corner (x delta from (0.5, 0.5) should be positive, y delta from (0.5, 0.5) should be negative);
255	 *
256	 * Once we know where the corner vertices are, we will remove them from the data set and iterate again over the
257	 * data set to identify all points that are part of edges connecting the edge corners. After the loop is done,
258	 * these vertices will have been associated with relevant edges and removed from the data sets.
259	 *
260	 * Once two iterations are complete, we're done.
261	 */
262	const unsigned int n_iterations = (run.inner[0] > 1) ? 2 : 1;
263
264	for (unsigned int n_iteration = 0; n_iteration < n_iterations; ++n_iteration)
265	{
266		_tess_coordinate_cartesian current_tl_point;
267		float					   current_tl_point_delta = 0.0f;
268		_tess_coordinate_cartesian current_tr_point;
269		float					   current_tr_point_delta = 0.0f;
270		_tess_coordinate_cartesian current_bl_point;
271		float					   current_bl_point_delta = 0.0f;
272		_tess_coordinate_cartesian current_br_point;
273		float					   current_br_point_delta = 0.0f;
274
275		/* Iterate over all points */
276		for (std::vector<_tess_coordinate_cartesian>::const_iterator coordinate_iterator = coordinates.begin();
277			 coordinate_iterator != coordinates.end(); coordinate_iterator++)
278		{
279			const _tess_coordinate_cartesian& coordinate = *coordinate_iterator;
280			float							  delta_x	= coordinate.x - 0.5f;
281			float							  delta_y	= coordinate.y - 0.5f;
282			float							  delta		 = deFloatSqrt(delta_x * delta_x + delta_y * delta_y);
283
284			/* top-left corner (x delta from (0.5, 0.5) should be negative, y delta from (0.5, 0.5) should be positive); */
285			if (delta_x <= 0.0f && delta_y >= 0.0f)
286			{
287				if (delta > current_tl_point_delta)
288				{
289					current_tl_point	   = coordinate;
290					current_tl_point_delta = delta;
291				}
292			}
293
294			/* top-right corner (x delta from (0.5, 0.5) should be positive, y delta from (0.5, 0.5) should be positive); */
295			if (delta_x >= 0.0f && delta_y >= 0.0f)
296			{
297				if (delta > current_tr_point_delta)
298				{
299					current_tr_point	   = coordinate;
300					current_tr_point_delta = delta;
301				}
302			}
303
304			/* bottom-left corner (x delta from (0.5, 0.5) should be negative, y delta from (0.5, 0.5) should be negative); */
305			if (delta_x <= 0.0f && delta_y <= 0.0f)
306			{
307				if (delta > current_bl_point_delta)
308				{
309					current_bl_point	   = coordinate;
310					current_bl_point_delta = delta;
311				}
312			}
313
314			/* bottom-right corner (x delta from (0.5, 0.5) should be positive, y delta from (0.5, 0.5) should be negative); */
315			if (delta_x >= 0.0f && delta_y <= 0.0f)
316			{
317				if (delta > current_br_point_delta)
318				{
319					current_br_point	   = coordinate;
320					current_br_point_delta = delta;
321				}
322			}
323		} /* for (all coordinates) */
324
325		/* Note: If any of the outer tessellation level is 1, at least
326		 *       two "current" points will refer to the same point.
327		 *
328		 * Now that we know where the corner vertices are, remove them from the data set
329		 */
330		const _tess_coordinate_cartesian* corner_coordinate_ptrs[] = { &current_tl_point, &current_tr_point,
331																	   &current_bl_point, &current_br_point };
332		const unsigned int n_corner_coordinate_ptrs =
333			sizeof(corner_coordinate_ptrs) / sizeof(corner_coordinate_ptrs[0]);
334
335		for (unsigned int n_corner_coordinate = 0; n_corner_coordinate < n_corner_coordinate_ptrs;
336			 ++n_corner_coordinate)
337		{
338			const _tess_coordinate_cartesian& corner_coordinate = *(corner_coordinate_ptrs[n_corner_coordinate]);
339			std::vector<_tess_coordinate_cartesian>::iterator iterator =
340				std::find(coordinates.begin(), coordinates.end(), corner_coordinate);
341
342			/* Iterator can be invalid at this point of any of the corner coordinates
343			 * referred more than once to the same point. */
344			if (iterator != coordinates.end())
345			{
346				coordinates.erase(iterator);
347			}
348		} /* for (all corner coordinates) */
349
350		/* Proceed with identification of coordinates describing the edges.
351		 *
352		 * Note: for inner quad, we need to subtract 2 segments connecting outer and inner coordinates */
353		float tl_tr_delta =
354			deFloatSqrt((current_tl_point.x - current_tr_point.x) * (current_tl_point.x - current_tr_point.x) +
355						(current_tl_point.y - current_tr_point.y) * (current_tl_point.y - current_tr_point.y));
356		float tr_br_delta =
357			deFloatSqrt((current_tr_point.x - current_br_point.x) * (current_tr_point.x - current_br_point.x) +
358						(current_tr_point.y - current_br_point.y) * (current_tr_point.y - current_br_point.y));
359		float br_bl_delta =
360			deFloatSqrt((current_br_point.x - current_bl_point.x) * (current_br_point.x - current_bl_point.x) +
361						(current_br_point.y - current_bl_point.y) * (current_br_point.y - current_bl_point.y));
362		float bl_tl_delta =
363			deFloatSqrt((current_bl_point.x - current_tl_point.x) * (current_bl_point.x - current_tl_point.x) +
364						(current_bl_point.y - current_tl_point.y) * (current_bl_point.y - current_tl_point.y));
365
366		_tess_edge tl_tr_edge(0, 0, tl_tr_delta);
367		_tess_edge tr_br_edge(0, 0, tr_br_delta);
368		_tess_edge br_bl_edge(0, 0, br_bl_delta);
369		_tess_edge bl_tl_edge(0, 0, bl_tl_delta);
370
371		tl_tr_edge.outermost_tess_level = run.outer[3];
372		tr_br_edge.outermost_tess_level = run.outer[2];
373		br_bl_edge.outermost_tess_level = run.outer[1];
374		bl_tl_edge.outermost_tess_level = run.outer[0];
375
376		if (n_iteration == 0)
377		{
378			tl_tr_edge.tess_level = run.outer[3];
379			tr_br_edge.tess_level = run.outer[2];
380			br_bl_edge.tess_level = run.outer[1];
381			bl_tl_edge.tess_level = run.outer[0];
382		}
383		else
384		{
385			tl_tr_edge.tess_level = run.inner[0] - 2 /* segments between inner and outer edges */;
386			br_bl_edge.tess_level = run.inner[0] - 2 /* segments between inner and outer edges */;
387			tr_br_edge.tess_level = run.inner[1] - 2 /* segments between inner and outer edges */;
388			bl_tl_edge.tess_level = run.inner[1] - 2 /* segments between inner and outer edges */;
389		}
390
391		/* Add corner points to edge descriptors. Do *NOT* add the same point twice, as
392		 * that will confuse verifyEdges() and cause incorrect failures.
393		 */
394		tl_tr_edge.points.push_back(current_tl_point);
395
396		if (!(current_tl_point == current_tr_point))
397		{
398			tl_tr_edge.points.push_back(current_tr_point);
399		}
400
401		tr_br_edge.points.push_back(current_tr_point);
402
403		if (!(current_tr_point == current_br_point))
404		{
405			tr_br_edge.points.push_back(current_br_point);
406		}
407
408		br_bl_edge.points.push_back(current_br_point);
409
410		if (!(current_br_point == current_bl_point))
411		{
412			br_bl_edge.points.push_back(current_bl_point);
413		}
414
415		bl_tl_edge.points.push_back(current_bl_point);
416
417		if (!(current_bl_point == current_tl_point))
418		{
419			bl_tl_edge.points.push_back(current_tl_point);
420		}
421
422		/* Identify points that lie on any of the edges considered */
423		_tess_edge*		   edge_ptrs[] = { &tl_tr_edge, &tr_br_edge, &br_bl_edge, &bl_tl_edge };
424		const unsigned int n_edge_ptrs = sizeof(edge_ptrs) / sizeof(edge_ptrs[0]);
425
426		for (unsigned int n_edge = 0; n_edge < n_edge_ptrs; ++n_edge)
427		{
428			_tess_edge& edge = *(edge_ptrs[n_edge]);
429
430			/* Degenerate edges will only consist of one point, for which the following
431			 * code needs not be executed */
432			if (edge.points.size() > 1)
433			{
434				/* Retrieve edge's start & end points */
435				_tess_coordinate_cartesian edge_start_point = edge.points[0];
436				_tess_coordinate_cartesian edge_end_point   = edge.points[1];
437
438				/* Iterate over the data set */
439				for (std::vector<_tess_coordinate_cartesian>::const_iterator iterator = coordinates.begin();
440					 iterator != coordinates.end(); iterator++)
441				{
442					const _tess_coordinate_cartesian& coordinate = *iterator;
443
444					if (isPointOnLine(edge_start_point, edge_end_point, coordinate) &&
445						!(edge_start_point == coordinate) && !(edge_end_point == coordinate))
446					{
447						/* Make sure the point has not already been added. If this happens,
448						 * it is very likely there is a bug in the way the implementation's
449						 * support of point mode */
450						if (std::find_if(edge.points.begin(), edge.points.end(),
451										 _comparator_exact_tess_coordinate_match(coordinate)) == edge.points.end())
452						{
453							edge.points.push_back(coordinate);
454						}
455						else
456						{
457							std::string primitive_mode_string =
458								TessellationShaderUtils::getESTokenForPrimitiveMode(run.primitive_mode);
459							std::string vertex_spacing_string =
460								TessellationShaderUtils::getESTokenForVertexSpacingMode(run.vertex_spacing);
461
462							m_testCtx.getLog()
463								<< tcu::TestLog::Message << "A duplicate vertex"
464								<< " (" << coordinate.x << ", " << coordinate.y
465								<< ") was found in set of coordinates generated for the following configuration:"
466								<< " inner tessellation levels:(" << run.inner[0] << ", " << run.inner[1]
467								<< ") outer tessellation levels:(" << run.outer[0] << ", " << run.outer[1] << ", "
468								<< run.outer[2] << ", " << run.outer[3] << ") primitive mode:" << primitive_mode_string
469								<< " vertex spacing mode:" << vertex_spacing_string << " point mode:yes"
470								<< tcu::TestLog::EndMessage;
471
472							TCU_FAIL("A duplicate vertex was found in generated tessellation coordinate set "
473									 "when point mode was used");
474						}
475					}
476				} /* for (all coordinates in the data set) */
477
478				/* Sort all points in the edge relative to the start point */
479				std::sort(edge.points.begin(), edge.points.end(), _comparator_relative_to_base_point(edge_start_point));
480			}
481		} /* for (all edges) */
482
483		/* Remove all coordinates associated to edges from the data set */
484		for (unsigned int n_edge = 0; n_edge < n_edge_ptrs; ++n_edge)
485		{
486			_tess_edge& edge = *(edge_ptrs[n_edge]);
487
488			for (std::vector<_tess_coordinate_cartesian>::const_iterator iterator = edge.points.begin();
489				 iterator != edge.points.end(); iterator++)
490			{
491				const _tess_coordinate_cartesian&				  coordinate = *iterator;
492				std::vector<_tess_coordinate_cartesian>::iterator dataset_iterator =
493					std::find(coordinates.begin(), coordinates.end(), coordinate);
494
495				if (dataset_iterator != coordinates.end())
496				{
497					coordinates.erase(dataset_iterator);
498				}
499			} /* for (all edge points) */
500		}	 /* for (all edges) */
501
502		/* Store the edges */
503		for (unsigned int n_edge = 0; n_edge < n_edge_ptrs; ++n_edge)
504		{
505			_tess_edge& edge = *(edge_ptrs[n_edge]);
506
507			result.push_back(edge);
508		}
509	} /* for (both iterations) */
510
511	return result;
512}
513
514/**  Takes data generated by tessellator for a specific run configuration and
515 *  converts it into a set of edges that correspond to subsequent triangles.
516 *  This function asserts that the run uses a 'triangles' primitive mode.
517 *
518 *  This function throws a TestError, should an error occur or the data is found
519 *  to be incorrect.
520 *
521 *  @param run Test run properties.
522 *
523 *  @return A vector storing found edges.
524 **/
525TessellationShaderVertexSpacing::_tess_edges TessellationShaderVertexSpacing::getEdgesForTrianglesTessellation(
526	const _run& run)
527{
528	DE_ASSERT(run.data_cartesian != DE_NULL);
529
530	/* Before we proceed, convert the raw arrayed data into a vector. We'll need to take items out
531	 * in an undefined order */
532	std::vector<_tess_coordinate_cartesian> coordinates;
533
534	for (unsigned int n_vertex = 0; n_vertex < run.n_vertices; ++n_vertex)
535	{
536		const float* vertex_data_cartesian = (const float*)run.data_cartesian + n_vertex * 2; /* components */
537
538		_tess_coordinate_cartesian cartesian;
539
540		cartesian.x = vertex_data_cartesian[0];
541		cartesian.y = vertex_data_cartesian[1];
542
543		coordinates.push_back(cartesian);
544	}
545
546	/* The function iterates over the data set generated by the tessellator and:
547	 *
548	 * 1) Finds three corner vertices. These coordinates are considered to correspond to corner vertices
549	 *    of the outermost triangle. The coordinates are removed from the data set. Note that it is an
550	 *    error if less than three coordinates are available in the data set at this point.
551	 * 2) Iterates over remaining points in the data set and associates them with AB, BC and CA edges.
552	 *    Each point found to be a part of any of the edges considered is removed from the data set.
553	 * 3) If more than 0 coordinates are still available in the data set at this point, implementation
554	 *    returns to step 1)
555	 *
556	 * After the implementation runs out of tessellation coordinates, a few sanity checks are executed
557	 * and the function returns to the caller.
558	 */
559	float		 base_tess_level  = 0.0f;
560	unsigned int tess_level_delta = 0;
561	_tess_edges  result;
562
563	/* Make sure to follow the cited extension spec language:
564	 *
565	 * If the inner tessellation level is one and any of the outer tessellation
566	 * levels is greater than one, the inner tessellation level is treated as
567	 * though it were originally specified as 1+epsilon and will be rounded up to
568	 * result in a two- or three-segment subdivision according to the
569	 * tessellation spacing.
570	 *
571	 */
572	float inner0_round_clamped_value = 0;
573
574	TessellationShaderUtils::getTessellationLevelAfterVertexSpacing(
575		run.vertex_spacing, run.inner[0], m_gl_max_tess_gen_level_value, DE_NULL, /* out_clamped */
576		&inner0_round_clamped_value);
577
578	if (inner0_round_clamped_value == 1.0f && (run.outer[0] > 1.0f || run.outer[1] > 1.0f || run.outer[2] > 1.0f))
579	{
580		TessellationShaderUtils::getTessellationLevelAfterVertexSpacing(
581			run.vertex_spacing, inner0_round_clamped_value + 1.0f /* epsilon */, m_gl_max_tess_gen_level_value,
582			DE_NULL, /* out_clamped */
583			&base_tess_level);
584	}
585	else
586	{
587		TessellationShaderUtils::getTessellationLevelAfterVertexSpacing(
588			run.vertex_spacing, run.inner[0], m_gl_max_tess_gen_level_value, DE_NULL, /* out_clamped */
589			&base_tess_level);
590	}
591
592	while (coordinates.size() > 0)
593	{
594		/* If we're using an odd tessellation level, it is an error if less than three coordinates are
595		 * available at this point.
596		 * If it's an even tessellation level, we must have at least three coordinates at hand OR
597		 * one, in which case the only coordinate left is the degenerate one. Should that happen,
598		 * it's time to leave. */
599		if ((((int)base_tess_level) % 2 == 0) && (coordinates.size() == 1))
600		{
601			/* We're left with the degenerate vertex. Leave the loop */
602			break;
603		}
604
605		if (coordinates.size() < 3)
606		{
607			TCU_FAIL("Could not extract three corner vertices that would make up a triangle");
608		}
609
610		/* Iterate over all vertices left and identify three corner vertices. For the outermost triangle,
611		 * these will be represented by (1, 0, 0), (0, 1, 0) and (0, 0, 1) barycentric coordinates, which
612		 * correspond to the following coordinates in Euclidean space: (0.5, 0), (1, 1), (0, 1) (as defined
613		 * in TessellationShaderUtils::convertCartesianCoordinatesToBarycentric() ).
614		 *
615		 * The idea here is to identify vertices that are the closest to the ideal coordinates, not necessarily
616		 * to find a perfect match. */
617		unsigned int curr_index			= 0;
618		float		 delta_v1			= 0.0f; /* barycentric:(1, 0, 0) -> Euclidean:(0.5, 0.0) */
619		float		 delta_v2			= 0.0f; /* barycentric:(0, 1, 0) -> Euclidean:(1.0, 1.0) */
620		float		 delta_v3			= 0.0f; /* barycentric:(0, 0, 1) -> Euclidean:(0.0, 1.0) */
621		bool		 is_first_iteration = true;
622		unsigned int selected_v1_index  = 0;
623		unsigned int selected_v2_index  = 0;
624		unsigned int selected_v3_index  = 0;
625
626		for (std::vector<_tess_coordinate_cartesian>::const_iterator iterator = coordinates.begin();
627			 iterator != coordinates.end(); iterator++, curr_index++)
628		{
629			const _tess_coordinate_cartesian& cartesian_coordinate = *iterator;
630
631			float curr_delta_v1 = deFloatSqrt((cartesian_coordinate.x - 0.5f) * (cartesian_coordinate.x - 0.5f) +
632											  (cartesian_coordinate.y - 0.0f) * (cartesian_coordinate.y - 0.0f));
633			float curr_delta_v2 = deFloatSqrt((cartesian_coordinate.x - 1.0f) * (cartesian_coordinate.x - 1.0f) +
634											  (cartesian_coordinate.y - 1.0f) * (cartesian_coordinate.y - 1.0f));
635			float curr_delta_v3 = deFloatSqrt((cartesian_coordinate.x - 0.0f) * (cartesian_coordinate.x - 0.0f) +
636											  (cartesian_coordinate.y - 1.0f) * (cartesian_coordinate.y - 1.0f));
637
638			if (is_first_iteration)
639			{
640				delta_v1 = curr_delta_v1;
641				delta_v2 = curr_delta_v2;
642				delta_v3 = curr_delta_v3;
643
644				/* No need to update selected vertex indices, since this is the very first iteration */
645				is_first_iteration = false;
646			}
647			else
648			{
649				if (curr_delta_v1 < delta_v1)
650				{
651					delta_v1		  = curr_delta_v1;
652					selected_v1_index = curr_index;
653				}
654
655				if (curr_delta_v2 < delta_v2)
656				{
657					delta_v2		  = curr_delta_v2;
658					selected_v2_index = curr_index;
659				}
660
661				if (curr_delta_v3 < delta_v3)
662				{
663					delta_v3		  = curr_delta_v3;
664					selected_v3_index = curr_index;
665				}
666			}
667		} /* for (all remaining coordinates) */
668
669		/* Extract the vertices out of the data set */
670		_tess_coordinate_cartesian corner_vertices[] = { *(coordinates.begin() + selected_v1_index),
671														 *(coordinates.begin() + selected_v2_index),
672														 *(coordinates.begin() + selected_v3_index) };
673		const unsigned int n_corner_vertices = sizeof(corner_vertices) / sizeof(corner_vertices[0]);
674
675		/* Remove the vertices from the data set */
676		for (unsigned int n_corner_vertex = 0; n_corner_vertex < n_corner_vertices; ++n_corner_vertex)
677		{
678			const _tess_coordinate_cartesian&				  vertex = corner_vertices[n_corner_vertex];
679			std::vector<_tess_coordinate_cartesian>::iterator iterator =
680				std::find(coordinates.begin(), coordinates.end(), vertex);
681
682			DE_ASSERT(iterator != coordinates.end());
683			if (iterator != coordinates.end())
684			{
685				coordinates.erase(iterator);
686			}
687		} /* for (all corner vertices) */
688
689		/* Now that we know where the corner vertices are, identify all points that lie
690		 * on edges defined by these vertices */
691		std::vector<_tess_coordinate_cartesian> edge_v1_v2_vertices;
692		std::vector<_tess_coordinate_cartesian> edge_v2_v3_vertices;
693		std::vector<_tess_coordinate_cartesian> edge_v3_v1_vertices;
694		const _tess_coordinate_cartesian&		v1 = corner_vertices[0];
695		const _tess_coordinate_cartesian&		v2 = corner_vertices[1];
696		const _tess_coordinate_cartesian&		v3 = corner_vertices[2];
697
698		for (std::vector<_tess_coordinate_cartesian>::const_iterator iterator = coordinates.begin();
699			 iterator != coordinates.end(); iterator++, curr_index++)
700		{
701			const _tess_coordinate_cartesian& cartesian_coordinate = *iterator;
702
703			if (isPointOnLine(v1, v2, cartesian_coordinate))
704			{
705				edge_v1_v2_vertices.push_back(*iterator);
706			}
707
708			if (isPointOnLine(v2, v3, cartesian_coordinate))
709			{
710				edge_v2_v3_vertices.push_back(*iterator);
711			}
712
713			if (isPointOnLine(v3, v1, cartesian_coordinate))
714			{
715				edge_v3_v1_vertices.push_back(*iterator);
716			}
717		} /* for (all coordinates in data set) */
718
719		/* Now that edge vertices have been identified, remove them from the data set */
720		const std::vector<_tess_coordinate_cartesian>* edge_ptrs[] = { &edge_v1_v2_vertices, &edge_v2_v3_vertices,
721																	   &edge_v3_v1_vertices };
722		const unsigned int n_edge_ptrs = sizeof(edge_ptrs) / sizeof(edge_ptrs[0]);
723
724		for (unsigned int n_edge_ptr = 0; n_edge_ptr < n_edge_ptrs; ++n_edge_ptr)
725		{
726			const std::vector<_tess_coordinate_cartesian>& edge			   = *edge_ptrs[n_edge_ptr];
727			const unsigned int							   n_edge_vertices = (const unsigned int)edge.size();
728
729			for (unsigned int n_edge_vertex = 0; n_edge_vertex < n_edge_vertices; ++n_edge_vertex)
730			{
731				const _tess_coordinate_cartesian&				  coordinate = edge[n_edge_vertex];
732				std::vector<_tess_coordinate_cartesian>::iterator iterator =
733					std::find(coordinates.begin(), coordinates.end(), coordinate);
734
735				if (iterator != coordinates.end())
736				{
737					coordinates.erase(iterator);
738				}
739			} /* for (all edge vertices) */
740		}	 /* for (all edges) */
741
742		/* Add corner coordinates to our vectors, but only if they are not
743		 * already there.
744		 */
745		if (std::find(edge_v1_v2_vertices.begin(), edge_v1_v2_vertices.end(), v1) == edge_v1_v2_vertices.end())
746		{
747			edge_v1_v2_vertices.push_back(v1);
748		}
749
750		if (std::find(edge_v1_v2_vertices.begin(), edge_v1_v2_vertices.end(), v2) == edge_v1_v2_vertices.end())
751		{
752			edge_v1_v2_vertices.push_back(v2);
753		}
754
755		if (std::find(edge_v2_v3_vertices.begin(), edge_v2_v3_vertices.end(), v2) == edge_v2_v3_vertices.end())
756		{
757			edge_v2_v3_vertices.push_back(v2);
758		}
759
760		if (std::find(edge_v2_v3_vertices.begin(), edge_v2_v3_vertices.end(), v3) == edge_v2_v3_vertices.end())
761		{
762			edge_v2_v3_vertices.push_back(v3);
763		}
764
765		if (std::find(edge_v3_v1_vertices.begin(), edge_v3_v1_vertices.end(), v3) == edge_v3_v1_vertices.end())
766		{
767			edge_v3_v1_vertices.push_back(v3);
768		}
769
770		if (std::find(edge_v3_v1_vertices.begin(), edge_v3_v1_vertices.end(), v1) == edge_v3_v1_vertices.end())
771		{
772			edge_v3_v1_vertices.push_back(v1);
773		}
774
775		/* Sort all points relative to corner point */
776		std::sort(edge_v1_v2_vertices.begin(), edge_v1_v2_vertices.end(), _comparator_relative_to_base_point(v1));
777
778		std::sort(edge_v2_v3_vertices.begin(), edge_v2_v3_vertices.end(), _comparator_relative_to_base_point(v2));
779
780		std::sort(edge_v3_v1_vertices.begin(), edge_v3_v1_vertices.end(), _comparator_relative_to_base_point(v3));
781
782		/* We now have all the data to update the result vector with new edge data */
783		for (unsigned int n_edge_ptr = 0; n_edge_ptr < n_edge_ptrs; ++n_edge_ptr)
784		{
785			/* Compute tessellation level values for the edge */
786			glw::GLfloat curr_tess_level	  = 0.0f;
787			glw::GLfloat outermost_tess_level = 0.0f;
788
789			if (tess_level_delta == 0)
790			{
791				switch (n_edge_ptr)
792				{
793				/* Assuming:
794				 *
795				 * v1 = (1, 0, 0)
796				 * v2 = (0, 1, 0)
797				 * v3 = (0, 0, 1)
798				 */
799				case 0: /* v1->v2 */
800				{
801					curr_tess_level		 = run.outer[2];
802					outermost_tess_level = run.outer[2];
803
804					break;
805				}
806
807				case 1: /* v2->v3 */
808				{
809					curr_tess_level		 = run.outer[0];
810					outermost_tess_level = run.outer[0];
811
812					break;
813				}
814
815				case 2: /* v3->v1 */
816				{
817					curr_tess_level		 = run.outer[1];
818					outermost_tess_level = run.outer[1];
819
820					break;
821				}
822
823				default:
824				{
825					DE_ASSERT("Invalid edge index");
826				}
827				} /* switch (n_edge_ptr) */
828			}
829			else
830			{
831				curr_tess_level		 = base_tess_level - (float)tess_level_delta;
832				outermost_tess_level = base_tess_level;
833			}
834
835			/* Convert internal representation to _tess_edge */
836			const std::vector<_tess_coordinate_cartesian>& edge				= *edge_ptrs[n_edge_ptr];
837			const _tess_coordinate_cartesian&			   edge_start_point = *edge.begin();
838			const _tess_coordinate_cartesian&			   edge_end_point   = *(edge.begin() + (edge.size() - 1));
839			float										   edge_length =
840				deFloatSqrt((edge_end_point.x - edge_start_point.x) * (edge_end_point.x - edge_start_point.x) +
841							(edge_end_point.y - edge_start_point.y) * (edge_end_point.y - edge_start_point.y));
842			_tess_edge result_edge(curr_tess_level, outermost_tess_level, edge_length);
843
844			for (std::vector<_tess_coordinate_cartesian>::const_iterator edge_point_iterator = edge.begin();
845				 edge_point_iterator != edge.end(); edge_point_iterator++)
846			{
847				const _tess_coordinate_cartesian& edge_point = *edge_point_iterator;
848
849				result_edge.points.push_back(edge_point);
850			}
851
852			/* Good to store the edge now */
853			result.push_back(result_edge);
854		} /* for (all edges) */
855
856		/* Moving on with next inner triangle. As per spec, reduce tessellation level by 2 */
857		tess_level_delta += 2;
858	} /* while (run.n_vertices > 0) */
859
860	return result;
861}
862
863/** Tells whether given two-dimensional point is located on a two-dimensional line defined
864 *  by two points.
865 *
866 *  @param line_v1 First vertex defining the line;
867 *  @param line_v2 Second vertex defining the line;
868 *  @param point   Point to check.
869 *
870 *  @return true  if the point was determned to be a part of the line,
871 *          false otherwise.
872 **/
873bool TessellationShaderVertexSpacing::isPointOnLine(const _tess_coordinate_cartesian& line_v1,
874													const _tess_coordinate_cartesian& line_v2,
875													const _tess_coordinate_cartesian& point)
876
877{
878	bool result = false;
879
880	/* Calculate distance from a point to a line passing through two points */
881	float Dx		  = line_v1.x - line_v2.x;
882	float Dy		  = line_v1.y - line_v2.y;
883	float denominator = deFloatSqrt(Dx * Dx + Dy * Dy);
884	float d = de::abs(Dy * point.x - Dx * point.y + line_v1.x * line_v2.y - line_v2.x * line_v1.y) / denominator;
885
886	if (de::abs(d) < epsilon)
887	{
888		result = true;
889	}
890
891	return result;
892}
893
894/** Initializes ES objects necessary to run the test. */
895void TessellationShaderVertexSpacing::initTest()
896{
897	/* Skip if required extensions are not supported. */
898	if (!m_is_tessellation_shader_supported)
899	{
900		throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED);
901	}
902
903	/* Initialize Utils instance */
904	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
905
906	m_utils = new TessellationShaderUtils(gl, this);
907
908	/* Initialize vertex array object */
909	gl.genVertexArrays(1, &m_vao_id);
910	GLU_EXPECT_NO_ERROR(gl.getError(), "Could not generate vertex array object");
911
912	gl.bindVertexArray(m_vao_id);
913	GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding vertex array object!");
914
915	/* Retrieve GL_MAX_TESS_GEN_LEVEL_EXT value */
916	gl.getIntegerv(m_glExtTokens.MAX_TESS_GEN_LEVEL, &m_gl_max_tess_gen_level_value);
917	GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() call failed for GL_MAX_TESS_GEN_LEVEL_EXT pname");
918
919	const _tessellation_shader_vertex_spacing vs_modes[] = { TESSELLATION_SHADER_VERTEX_SPACING_EQUAL,
920															 TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_EVEN,
921															 TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD,
922															 TESSELLATION_SHADER_VERTEX_SPACING_DEFAULT };
923	const unsigned int n_vs_modes = sizeof(vs_modes) / sizeof(vs_modes[0]);
924
925	const _tessellation_primitive_mode primitive_modes[] = { TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES,
926															 TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES,
927															 TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS };
928	const unsigned int n_primitive_modes = sizeof(primitive_modes) / sizeof(primitive_modes[0]);
929
930	/* Iterate through all primitive modes */
931	for (unsigned int n_primitive_mode = 0; n_primitive_mode < n_primitive_modes; ++n_primitive_mode)
932	{
933		_tessellation_primitive_mode primitive_mode = primitive_modes[n_primitive_mode];
934
935		/* Generate tessellation level set for current primitive mode */
936		_tessellation_levels_set tess_levels_set;
937
938		tess_levels_set = TessellationShaderUtils::getTessellationLevelSetForPrimitiveMode(
939			primitive_mode, m_gl_max_tess_gen_level_value,
940			TESSELLATION_LEVEL_SET_FILTER_INNER_AND_OUTER_LEVELS_USE_DIFFERENT_VALUES);
941
942		/* Iterate through all vertex spacing modes */
943		for (unsigned int n_vs_mode = 0; n_vs_mode < n_vs_modes; ++n_vs_mode)
944		{
945			_tessellation_shader_vertex_spacing vs_mode = vs_modes[n_vs_mode];
946
947			/* Iterate through all tessellation level combinations */
948			for (_tessellation_levels_set_const_iterator tess_levels_set_iterator = tess_levels_set.begin();
949				 tess_levels_set_iterator != tess_levels_set.end(); tess_levels_set_iterator++)
950			{
951				const _tessellation_levels& tess_levels = *tess_levels_set_iterator;
952				_run						run;
953
954				/* Skip border cases that this test cannot handle */
955				if (primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS &&
956					vs_mode == TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD &&
957					(tess_levels.inner[0] <= 1 || tess_levels.inner[1] <= 1))
958				{
959					continue;
960				}
961
962				/* Fill run descriptor */
963				memcpy(run.inner, tess_levels.inner, sizeof(run.inner));
964				memcpy(run.outer, tess_levels.outer, sizeof(run.outer));
965
966				run.primitive_mode = primitive_mode;
967				run.vertex_spacing = vs_mode;
968
969				/* Retrieve vertex data for both passes */
970				run.n_vertices = m_utils->getAmountOfVerticesGeneratedByTessellator(
971					run.primitive_mode, run.inner, run.outer, run.vertex_spacing, true); /* is_point_mode_enabled */
972
973				if (run.n_vertices == 0)
974				{
975					std::string primitive_mode_string =
976						TessellationShaderUtils::getESTokenForPrimitiveMode(primitive_mode);
977					std::string vs_mode_string = TessellationShaderUtils::getESTokenForVertexSpacingMode(vs_mode);
978
979					m_testCtx.getLog() << tcu::TestLog::Message << "No vertices were generated by tessellator for: "
980																   "inner tess levels:"
981																   "["
982									   << run.inner[0] << ", " << run.inner[1] << "]"
983																				  ", outer tess levels:"
984																				  "["
985									   << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", "
986									   << run.outer[3] << "]"
987														  ", primitive mode: "
988									   << primitive_mode_string << ", vertex spacing: " << vs_mode_string
989									   << tcu::TestLog::EndMessage;
990
991					TCU_FAIL("Zero vertices were generated by tessellator");
992				}
993
994				/* Retrieve the data buffers */
995				run.data = m_utils->getDataGeneratedByTessellator(
996					run.inner, true, /* is_point_mode_enabled */
997					run.primitive_mode, TESSELLATION_SHADER_VERTEX_ORDERING_CCW, run.vertex_spacing, run.outer);
998
999				/* 'triangles' tessellation data is expressed in barycentric coordinates. Before we can
1000				 * continue, we need to convert the data to Euclidean space */
1001				if (run.primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES)
1002				{
1003					run.data_cartesian = new float[run.n_vertices * 2 /* components */];
1004
1005					for (unsigned int n_vertex = 0; n_vertex < run.n_vertices; ++n_vertex)
1006					{
1007						const float* barycentric_vertex_data =
1008							(const float*)(&run.data[0]) + n_vertex * 3;						  /* components */
1009						float* cartesian_vertex_data = (float*)run.data_cartesian + n_vertex * 2; /* components */
1010
1011						TessellationShaderUtils::convertBarycentricCoordinatesToCartesian(barycentric_vertex_data,
1012																						  cartesian_vertex_data);
1013					}
1014				} /* if (run.primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES) */
1015				else
1016				{
1017					run.data_cartesian = DE_NULL;
1018				}
1019
1020				/* Store the run data */
1021				m_runs.push_back(run);
1022			} /* for (all tessellation level values ) */
1023		}	 /* for (all primitive modes) */
1024	}		  /* for (all vertex spacing modes) */
1025}
1026
1027/** Executes the test.
1028 *
1029 *  Sets the test result to QP_TEST_RESULT_FAIL if the test failed, QP_TEST_RESULT_PASS otherwise.
1030 *
1031 *  Note the function throws exception should an error occur!
1032 *
1033 *  @return STOP if the test has finished, CONTINUE to indicate iterate() should be called once again.
1034 **/
1035tcu::TestNode::IterateResult TessellationShaderVertexSpacing::iterate(void)
1036{
1037	/* Do not execute if required extensions are not supported. */
1038	if (!m_is_tessellation_shader_supported)
1039	{
1040		throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED);
1041	}
1042
1043	/* Initialize the test */
1044	initTest();
1045
1046	/* Iterate through all runs */
1047	for (_runs_const_iterator run_iterator = m_runs.begin(); run_iterator != m_runs.end(); run_iterator++)
1048	{
1049		_tess_edges edges;
1050		const _run& run = *run_iterator;
1051
1052		switch (run.primitive_mode)
1053		{
1054		case TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES:
1055		{
1056			edges = getEdgesForIsolinesTessellation(run);
1057
1058			break;
1059		}
1060
1061		case TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS:
1062		{
1063			edges = getEdgesForQuadsTessellation(run);
1064
1065			break;
1066		}
1067
1068		case TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES:
1069		{
1070			edges = getEdgesForTrianglesTessellation(run);
1071
1072			break;
1073		}
1074
1075		default:
1076		{
1077			TCU_FAIL("Unrecognized primitive mode");
1078		}
1079		} /* switch (run.primitive_mode) */
1080
1081		verifyEdges(edges, run);
1082	} /* for (all runs) */
1083
1084	/* All done */
1085	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
1086	return STOP;
1087}
1088
1089/* Verifies that user-provided edges follow the vertex spacing convention, as
1090 * defined in the extension specification.
1091 *
1092 * This function throws a TestError, should an error occur or the data is found
1093 * to be incorrect.
1094 *
1095 * @param edges Data of all edges to run the check against.
1096 * @param run   Test run properties.
1097 **/
1098void TessellationShaderVertexSpacing::verifyEdges(const _tess_edges& edges, const _run& run)
1099{
1100	/* Cache strings that may be used by logging the routines */
1101	const std::string primitive_mode_string = TessellationShaderUtils::getESTokenForPrimitiveMode(run.primitive_mode);
1102	const std::string vertex_spacing_string =
1103		TessellationShaderUtils::getESTokenForVertexSpacingMode(run.vertex_spacing);
1104
1105	/* Iterate through all edges */
1106	unsigned int n_edge = 0;
1107
1108	for (_tess_edges_const_iterator edges_iterator = edges.begin(); edges_iterator != edges.end();
1109		 edges_iterator++, n_edge++)
1110	{
1111		const _tess_edge&		edge									  = *edges_iterator;
1112		float					edge_clamped_tess_level					  = 0.0f;
1113		float					edge_clamped_rounded_tess_level			  = 0.0f;
1114		float					outermost_edge_tess_level_clamped_rounded = 0.0f;
1115		_tess_coordinate_deltas segment_deltas;
1116
1117		TessellationShaderUtils::getTessellationLevelAfterVertexSpacing(
1118			run.vertex_spacing, edge.outermost_tess_level, m_gl_max_tess_gen_level_value, DE_NULL, /* out_clamped */
1119			&outermost_edge_tess_level_clamped_rounded);
1120
1121		/* Retrieve amount of segments the edge should consist of */
1122		TessellationShaderUtils::getTessellationLevelAfterVertexSpacing(
1123			run.vertex_spacing, edge.tess_level, m_gl_max_tess_gen_level_value, &edge_clamped_tess_level,
1124			&edge_clamped_rounded_tess_level);
1125
1126		/* Take two subsequent points if they are available. Vertex spacing has no meaning
1127		 * in a world of degenerate edges, so skip the check if we have just encountered one.
1128		 */
1129		const unsigned int n_points = (const unsigned int)edge.points.size();
1130
1131		if (n_points < 2)
1132		{
1133			continue;
1134		}
1135
1136		/* Compute segment deltas */
1137		for (unsigned int n_base_point = 0; n_base_point < n_points - 1; n_base_point++)
1138		{
1139			const _tess_coordinate_cartesian& point_a = edge.points[n_base_point + 0];
1140			const _tess_coordinate_cartesian& point_b = edge.points[n_base_point + 1];
1141
1142			/* Calculate the distance between the points */
1143			float distance_nonsqrt = 0.0f;
1144			float distance		   = 0.0f;
1145
1146			distance_nonsqrt =
1147				(point_a.x - point_b.x) * (point_a.x - point_b.x) + (point_a.y - point_b.y) * (point_a.y - point_b.y);
1148
1149			distance = deFloatSqrt(distance_nonsqrt);
1150
1151			/* Check if the distance is not already recognized. */
1152			_tess_coordinate_deltas_iterator deltas_iterator;
1153
1154			for (deltas_iterator = segment_deltas.begin(); deltas_iterator != segment_deltas.end(); deltas_iterator++)
1155			{
1156				if (de::abs(deltas_iterator->delta - distance) < epsilon)
1157				{
1158					/* Increment the counter and leave */
1159					deltas_iterator->counter++;
1160
1161					break;
1162				}
1163			}
1164
1165			if (deltas_iterator == segment_deltas.end())
1166			{
1167				/* This is the first time we're encountering a segment of this specific length. */
1168				_tess_coordinate_delta new_item;
1169
1170				new_item.counter = 1;
1171				new_item.delta   = distance;
1172
1173				segment_deltas.push_back(new_item);
1174			}
1175		} /* for (all base points) */
1176
1177		DE_ASSERT(segment_deltas.size() != 0);
1178
1179		switch (run.vertex_spacing)
1180		{
1181		case TESSELLATION_SHADER_VERTEX_SPACING_DEFAULT:
1182		case TESSELLATION_SHADER_VERTEX_SPACING_EQUAL:
1183		{
1184			/* For equal vertex spacings, we should end up with a single _tess_coordinate_delta instance
1185			 * of a predefined length, describing exactly edge_clamped_rounded_tess_level invocations */
1186			float expected_delta = edge.edge_length / edge_clamped_rounded_tess_level;
1187
1188			if (segment_deltas.size() != 1)
1189			{
1190				m_testCtx.getLog() << tcu::TestLog::Message
1191								   << "More than one segment delta was generated for the following tessellation"
1192									  " configuration: "
1193									  "primitive mode:"
1194								   << primitive_mode_string << "vertex spacing mode:" << vertex_spacing_string
1195								   << "inner tessellation levels: (" << run.inner[0] << ", " << run.inner[1]
1196								   << ")"
1197									  ", outer tessellation levels: ("
1198								   << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", "
1199								   << run.outer[3] << ")" << tcu::TestLog::EndMessage;
1200
1201				TCU_FAIL("Equal vertex spacing mode tessellation generated segment edges of varying lengths, "
1202						 "whereas only one was expected.");
1203			}
1204
1205			if (de::abs(segment_deltas[0].delta - expected_delta) > epsilon)
1206			{
1207				m_testCtx.getLog() << tcu::TestLog::Message << "Invalid segment delta (expected:" << expected_delta
1208								   << ", found: " << segment_deltas[0].delta
1209								   << ") was generated for the following tessellation configuration: "
1210									  "primitive mode:"
1211								   << primitive_mode_string << "vertex spacing mode:" << vertex_spacing_string
1212								   << "inner tessellation levels: (" << run.inner[0] << ", " << run.inner[1]
1213								   << ")"
1214									  ", outer tessellation levels: ("
1215								   << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", "
1216								   << run.outer[3] << ")" << tcu::TestLog::EndMessage;
1217
1218				TCU_FAIL("Invalid delta between segments generated by the tessellator configured to run "
1219						 "in equal vertex spacing mode");
1220			}
1221
1222			if (segment_deltas[0].counter != (unsigned int)edge_clamped_rounded_tess_level)
1223			{
1224				m_testCtx.getLog() << tcu::TestLog::Message
1225								   << "Invalid amount of segments (expected:" << (int)edge_clamped_rounded_tess_level
1226								   << ", found: " << segment_deltas[0].counter
1227								   << ") "
1228									  "was generated for the following tessellation configuration: "
1229									  "primitive mode:"
1230								   << primitive_mode_string << "vertex spacing mode:" << vertex_spacing_string
1231								   << "inner tessellation levels: (" << run.inner[0] << ", " << run.inner[1]
1232								   << ")"
1233									  ", outer tessellation levels: ("
1234								   << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", "
1235								   << run.outer[3] << ")" << tcu::TestLog::EndMessage;
1236
1237				TCU_FAIL("Invalid amount of segments generated for equal vertex spacing mode");
1238			}
1239
1240			break;
1241		} /* default/equal vertex spacing */
1242
1243		case TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_EVEN:
1244		case TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD:
1245		{
1246			/* For fractional vertex spacings, situation is more tricky. The extension specification
1247			 * is very liberal when it comes to defining segment lengths. Hence the only thing we
1248			 * should be testing here is that:
1249			 *
1250			 * a) No more than 2 deltas are generated for a single edge.
1251			 *
1252			 * b) If a single delta is generated, it should:
1253			 *
1254			 *    1. define exactly edge_clamped_rounded_tess_level-2 segments if
1255			 *    |edge_clamped_rounded_tess_level - edge_clamped_tess_level| == 2.0f,
1256			 *
1257			 *    2. define exactly edge_clamped_rounded_tess_level segments otherwise.
1258			 *
1259			 * c) If two deltas are generated, one of them should define 2 segments, and the other
1260			 *    one should define edge_clamped_rounded_tess_level-2 segments.
1261			 */
1262
1263			if (segment_deltas.size() > 2)
1264			{
1265				m_testCtx.getLog() << tcu::TestLog::Message << "More than two segment deltas (" << segment_deltas.size()
1266								   << ") were generated for the following tessellation configuration: "
1267									  "primitive mode:"
1268								   << primitive_mode_string << "vertex spacing mode:" << vertex_spacing_string
1269								   << "inner tessellation levels: (" << run.inner[0] << ", " << run.inner[1]
1270								   << ")"
1271									  ", outer tessellation levels: ("
1272								   << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", "
1273								   << run.outer[3] << ")" << tcu::TestLog::EndMessage;
1274
1275				TCU_FAIL("Fractional spacing mode tessellated edges to segments of more than two "
1276						 "differentiable lengths");
1277			}
1278
1279			if (segment_deltas.size() == 1)
1280			{
1281				int expected_counter = 0;
1282
1283				if (run.primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES)
1284				{
1285					/* With each triangle level, 2 segments go out. Each triangle consists of 3 edges */
1286					expected_counter = (int)outermost_edge_tess_level_clamped_rounded - 2 * (n_edge / 3);
1287				}
1288				else
1289				{
1290					expected_counter = (int)edge_clamped_rounded_tess_level;
1291					if (run.primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS &&
1292						run.vertex_spacing == TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD)
1293					{
1294						/* 8 edges expected in total; we should expect 2 segments less for the inner quad */
1295						expected_counter = (int)edge_clamped_rounded_tess_level - 2 * (n_edge / 4);
1296					}
1297
1298					/* For degenerate cases, always assume exactly one segment should be generated */
1299					if (edge.tess_level <= 0.0f)
1300					{
1301						expected_counter = 1;
1302					}
1303				}
1304
1305				if (segment_deltas[0].counter != (unsigned int)expected_counter)
1306				{
1307					m_testCtx.getLog() << tcu::TestLog::Message
1308									   << "Invalid amount of segments (expected:" << expected_counter
1309									   << ", found: " << segment_deltas[0].counter
1310									   << ") "
1311										  "was generated for the following tessellation configuration: "
1312										  "primitive mode:"
1313									   << primitive_mode_string << "vertex spacing mode:" << vertex_spacing_string
1314									   << "inner tessellation levels: (" << run.inner[0] << ", " << run.inner[1]
1315									   << ")"
1316										  ", outer tessellation levels: ("
1317									   << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", "
1318									   << run.outer[3] << ")" << tcu::TestLog::EndMessage;
1319
1320					TCU_FAIL("Invalid amount of segments generated for fractional vertex spacing mode");
1321				}
1322			}
1323			else
1324			{
1325				DE_ASSERT(segment_deltas.size() == 2);
1326
1327				if (!((segment_deltas[0].counter == 2 &&
1328					   segment_deltas[1].counter == ((unsigned int)edge_clamped_rounded_tess_level - 2)) ||
1329					  (segment_deltas[1].counter == 2 &&
1330					   segment_deltas[0].counter == ((unsigned int)edge_clamped_rounded_tess_level - 2))))
1331				{
1332					m_testCtx.getLog() << tcu::TestLog::Message << "Invalid number of segments with different deltas ("
1333									   << segment_deltas[0].delta << " and " << segment_deltas[1].delta
1334									   << ") was generated. "
1335										  "primitive mode:"
1336									   << primitive_mode_string << "vertex spacing mode:" << vertex_spacing_string
1337									   << "inner tessellation levels: (" << run.inner[0] << ", " << run.inner[1]
1338									   << ")"
1339										  ", outer tessellation levels: ("
1340									   << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", "
1341									   << run.outer[3] << ")" << tcu::TestLog::EndMessage;
1342
1343					TCU_FAIL("Equal amount of segments was generated for segments of different deltas");
1344				}
1345
1346				if (segment_deltas[0].counter != 2 && segment_deltas[1].counter != 2)
1347				{
1348					m_testCtx.getLog() << tcu::TestLog::Message
1349									   << "Neither of the segments generated by the tessellator was defined "
1350										  "exactly twice."
1351										  "primitive mode:"
1352									   << primitive_mode_string << "vertex spacing mode:" << vertex_spacing_string
1353									   << "inner tessellation levels: (" << run.inner[0] << ", " << run.inner[1]
1354									   << ")"
1355										  ", outer tessellation levels: ("
1356									   << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", "
1357									   << run.outer[3] << ")" << tcu::TestLog::EndMessage;
1358
1359					TCU_FAIL("Neither of the generated segments was repeated exactly twice "
1360							 "for fractional vertex spacing mode");
1361				}
1362			}
1363
1364			break;
1365		} /* fractional even/odd vertex spacing types */
1366
1367		default:
1368		{
1369			TCU_FAIL("Unrecognized vertex spacing mode");
1370		}
1371		} /* switch (run.vertex_spacing) */
1372	}	 /* for (all edges) */
1373}
1374}
1375
1376/* namespace glcts */
1377