Home | History | Annotate | Download | only in tessellation_shader
      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. */
     34 const float epsilon = 1e-3f;
     35 
     36 namespace 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  **/
     44 bool 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  **/
     63 bool 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  **/
     80 TessellationShaderVertexSpacing::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  **/
     98 bool 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  **/
    112 bool 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. */
    118 void 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  **/
    153 TessellationShaderVertexSpacing::_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  **/
    223 TessellationShaderVertexSpacing::_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  **/
    525 TessellationShaderVertexSpacing::_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  **/
    873 bool 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. */
    895 void 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  **/
   1035 tcu::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  **/
   1098 void 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