Fix malformed assets, use DE_FATAL
[platform/upstream/VK-GL-CTS.git] / external / openglcts / modules / glesext / tessellation_shader / esextcTessellationShaderVertexSpacing.cpp
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 = (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  **/
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 = (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 */