• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*-------------------------------------------------------------------------
2  * OpenGL Conformance Test Suite
3  * -----------------------------
4  *
5  * Copyright (c) 2014-2016 The Khronos Group Inc.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  */ /*!
20  * \file
21  * \brief
22  */ /*-------------------------------------------------------------------*/
23 
24 #include "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  **/
operator ==(const TessellationShaderVertexSpacing::_tess_coordinate & in) const44 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  **/
operator ==(const TessellationShaderVertexSpacing::_tess_coordinate_cartesian & in) const63 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  **/
TessellationShaderVertexSpacing(Context & context,const ExtParameters & extParams)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  **/
compareEdgeByX(_tess_coordinate_cartesian a,_tess_coordinate_cartesian b)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  **/
compareEdgeByY(_tess_coordinate_cartesian a,_tess_coordinate_cartesian b)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. */
deinit()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  **/
getEdgesForIsolinesTessellation(const _run & run)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  **/
getEdgesForQuadsTessellation(const _run & run)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  **/
getEdgesForTrianglesTessellation(const _run & run)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 = (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_FATAL("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  **/
isPointOnLine(const _tess_coordinate_cartesian & line_v1,const _tess_coordinate_cartesian & line_v2,const _tess_coordinate_cartesian & point)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. */
initTest()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  **/
iterate(void)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  **/
verifyEdges(const _tess_edges & edges,const _run & run)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 = (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