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 quick 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[] = {
920         TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_EVEN,
921         TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD, TESSELLATION_SHADER_VERTEX_SPACING_DEFAULT};
922     const unsigned int n_vs_modes = sizeof(vs_modes) / sizeof(vs_modes[0]);
923 
924     const _tessellation_primitive_mode primitive_modes[] = {TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES,
925                                                             TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES,
926                                                             TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS};
927     const unsigned int n_primitive_modes                 = sizeof(primitive_modes) / sizeof(primitive_modes[0]);
928 
929     /* Iterate through all primitive modes */
930     for (unsigned int n_primitive_mode = 0; n_primitive_mode < n_primitive_modes; ++n_primitive_mode)
931     {
932         _tessellation_primitive_mode primitive_mode = primitive_modes[n_primitive_mode];
933 
934         /* Generate tessellation level set for current primitive mode */
935         _tessellation_levels_set tess_levels_set;
936 
937         tess_levels_set = TessellationShaderUtils::getTessellationLevelSetForPrimitiveMode(
938             primitive_mode, m_gl_max_tess_gen_level_value,
939             TESSELLATION_LEVEL_SET_FILTER_INNER_AND_OUTER_LEVELS_USE_DIFFERENT_VALUES);
940 
941         /* Iterate through all vertex spacing modes */
942         for (unsigned int n_vs_mode = 0; n_vs_mode < n_vs_modes; ++n_vs_mode)
943         {
944             _tessellation_shader_vertex_spacing vs_mode = vs_modes[n_vs_mode];
945 
946             /* Iterate through all tessellation level combinations */
947             for (_tessellation_levels_set_const_iterator tess_levels_set_iterator = tess_levels_set.begin();
948                  tess_levels_set_iterator != tess_levels_set.end(); tess_levels_set_iterator++)
949             {
950                 const _tessellation_levels &tess_levels = *tess_levels_set_iterator;
951                 _run run;
952 
953                 /* Skip border cases that this test cannot handle */
954                 if (primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS &&
955                     vs_mode == TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD &&
956                     (tess_levels.inner[0] <= 1 || tess_levels.inner[1] <= 1))
957                 {
958                     continue;
959                 }
960 
961                 /* Fill run descriptor */
962                 memcpy(run.inner, tess_levels.inner, sizeof(run.inner));
963                 memcpy(run.outer, tess_levels.outer, sizeof(run.outer));
964 
965                 run.primitive_mode = primitive_mode;
966                 run.vertex_spacing = vs_mode;
967 
968                 /* Retrieve vertex data for both passes */
969                 run.n_vertices = m_utils->getAmountOfVerticesGeneratedByTessellator(
970                     run.primitive_mode, run.inner, run.outer, run.vertex_spacing, true); /* is_point_mode_enabled */
971 
972                 if (run.n_vertices == 0)
973                 {
974                     std::string primitive_mode_string =
975                         TessellationShaderUtils::getESTokenForPrimitiveMode(primitive_mode);
976                     std::string vs_mode_string = TessellationShaderUtils::getESTokenForVertexSpacingMode(vs_mode);
977 
978                     m_testCtx.getLog() << tcu::TestLog::Message
979                                        << "No vertices were generated by tessellator for: "
980                                           "inner tess levels:"
981                                           "["
982                                        << run.inner[0] << ", " << run.inner[1]
983                                        << "]"
984                                           ", outer tess levels:"
985                                           "["
986                                        << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", "
987                                        << run.outer[3]
988                                        << "]"
989                                           ", primitive mode: "
990                                        << primitive_mode_string << ", vertex spacing: " << vs_mode_string
991                                        << tcu::TestLog::EndMessage;
992 
993                     TCU_FAIL("Zero vertices were generated by tessellator");
994                 }
995 
996                 /* Retrieve the data buffers */
997                 run.data = m_utils->getDataGeneratedByTessellator(
998                     run.inner, true, /* is_point_mode_enabled */
999                     run.primitive_mode, TESSELLATION_SHADER_VERTEX_ORDERING_CCW, run.vertex_spacing, run.outer);
1000 
1001                 /* 'triangles' tessellation data is expressed in barycentric coordinates. Before we can
1002                  * continue, we need to convert the data to Euclidean space */
1003                 if (run.primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES)
1004                 {
1005                     run.data_cartesian = new float[run.n_vertices * 2 /* components */];
1006 
1007                     for (unsigned int n_vertex = 0; n_vertex < run.n_vertices; ++n_vertex)
1008                     {
1009                         const float *barycentric_vertex_data =
1010                             (const float *)(&run.data[0]) + n_vertex * 3;                          /* components */
1011                         float *cartesian_vertex_data = (float *)run.data_cartesian + n_vertex * 2; /* components */
1012 
1013                         TessellationShaderUtils::convertBarycentricCoordinatesToCartesian(barycentric_vertex_data,
1014                                                                                           cartesian_vertex_data);
1015                     }
1016                 } /* if (run.primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES) */
1017                 else
1018                 {
1019                     run.data_cartesian = DE_NULL;
1020                 }
1021 
1022                 /* Store the run data */
1023                 m_runs.push_back(run);
1024             } /* for (all tessellation level values ) */
1025         }     /* for (all primitive modes) */
1026     }         /* for (all vertex spacing modes) */
1027 }
1028 
1029 /** Executes the test.
1030  *
1031  *  Sets the test result to QP_TEST_RESULT_FAIL if the test failed, QP_TEST_RESULT_PASS otherwise.
1032  *
1033  *  Note the function throws exception should an error occur!
1034  *
1035  *  @return STOP if the test has finished, CONTINUE to indicate iterate() should be called once again.
1036  **/
iterate(void)1037 tcu::TestNode::IterateResult TessellationShaderVertexSpacing::iterate(void)
1038 {
1039     /* Do not execute if required extensions are not supported. */
1040     if (!m_is_tessellation_shader_supported)
1041     {
1042         throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED);
1043     }
1044 
1045     /* Initialize the test */
1046     initTest();
1047 
1048     /* Iterate through all runs */
1049     for (_runs_const_iterator run_iterator = m_runs.begin(); run_iterator != m_runs.end(); run_iterator++)
1050     {
1051         _tess_edges edges;
1052         const _run &run = *run_iterator;
1053 
1054         switch (run.primitive_mode)
1055         {
1056         case TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES:
1057         {
1058             edges = getEdgesForIsolinesTessellation(run);
1059 
1060             break;
1061         }
1062 
1063         case TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS:
1064         {
1065             edges = getEdgesForQuadsTessellation(run);
1066 
1067             break;
1068         }
1069 
1070         case TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES:
1071         {
1072             edges = getEdgesForTrianglesTessellation(run);
1073 
1074             break;
1075         }
1076 
1077         default:
1078         {
1079             TCU_FAIL("Unrecognized primitive mode");
1080         }
1081         } /* switch (run.primitive_mode) */
1082 
1083         verifyEdges(edges, run);
1084     } /* for (all runs) */
1085 
1086     /* All done */
1087     m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
1088     return STOP;
1089 }
1090 
1091 /* Verifies that user-provided edges follow the vertex spacing convention, as
1092  * defined in the extension specification.
1093  *
1094  * This function throws a TestError, should an error occur or the data is found
1095  * to be incorrect.
1096  *
1097  * @param edges Data of all edges to run the check against.
1098  * @param run   Test run properties.
1099  **/
verifyEdges(const _tess_edges & edges,const _run & run)1100 void TessellationShaderVertexSpacing::verifyEdges(const _tess_edges &edges, const _run &run)
1101 {
1102     /* Cache strings that may be used by logging the routines */
1103     const std::string primitive_mode_string = TessellationShaderUtils::getESTokenForPrimitiveMode(run.primitive_mode);
1104     const std::string vertex_spacing_string =
1105         TessellationShaderUtils::getESTokenForVertexSpacingMode(run.vertex_spacing);
1106 
1107     /* Iterate through all edges */
1108     unsigned int n_edge = 0;
1109 
1110     for (_tess_edges_const_iterator edges_iterator = edges.begin(); edges_iterator != edges.end();
1111          edges_iterator++, n_edge++)
1112     {
1113         const _tess_edge &edge                          = *edges_iterator;
1114         float edge_clamped_tess_level                   = 0.0f;
1115         float edge_clamped_rounded_tess_level           = 0.0f;
1116         float outermost_edge_tess_level_clamped_rounded = 0.0f;
1117         _tess_coordinate_deltas segment_deltas;
1118 
1119         TessellationShaderUtils::getTessellationLevelAfterVertexSpacing(
1120             run.vertex_spacing, edge.outermost_tess_level, m_gl_max_tess_gen_level_value, DE_NULL, /* out_clamped */
1121             &outermost_edge_tess_level_clamped_rounded);
1122 
1123         /* Retrieve amount of segments the edge should consist of */
1124         TessellationShaderUtils::getTessellationLevelAfterVertexSpacing(
1125             run.vertex_spacing, edge.tess_level, m_gl_max_tess_gen_level_value, &edge_clamped_tess_level,
1126             &edge_clamped_rounded_tess_level);
1127 
1128         /* Take two subsequent points if they are available. Vertex spacing has no meaning
1129          * in a world of degenerate edges, so skip the check if we have just encountered one.
1130          */
1131         const unsigned int n_points = (unsigned int)edge.points.size();
1132 
1133         if (n_points < 2)
1134         {
1135             continue;
1136         }
1137 
1138         /* Compute segment deltas */
1139         for (unsigned int n_base_point = 0; n_base_point < n_points - 1; n_base_point++)
1140         {
1141             const _tess_coordinate_cartesian &point_a = edge.points[n_base_point + 0];
1142             const _tess_coordinate_cartesian &point_b = edge.points[n_base_point + 1];
1143 
1144             /* Calculate the distance between the points */
1145             float distance_nonsqrt = 0.0f;
1146             float distance         = 0.0f;
1147 
1148             distance_nonsqrt =
1149                 (point_a.x - point_b.x) * (point_a.x - point_b.x) + (point_a.y - point_b.y) * (point_a.y - point_b.y);
1150 
1151             distance = deFloatSqrt(distance_nonsqrt);
1152 
1153             /* Check if the distance is not already recognized. */
1154             _tess_coordinate_deltas_iterator deltas_iterator;
1155 
1156             for (deltas_iterator = segment_deltas.begin(); deltas_iterator != segment_deltas.end(); deltas_iterator++)
1157             {
1158                 if (de::abs(deltas_iterator->delta - distance) < epsilon)
1159                 {
1160                     /* Increment the counter and leave */
1161                     deltas_iterator->counter++;
1162 
1163                     break;
1164                 }
1165             }
1166 
1167             if (deltas_iterator == segment_deltas.end())
1168             {
1169                 /* This is the first time we're encountering a segment of this specific length. */
1170                 _tess_coordinate_delta new_item;
1171 
1172                 new_item.counter = 1;
1173                 new_item.delta   = distance;
1174 
1175                 segment_deltas.push_back(new_item);
1176             }
1177         } /* for (all base points) */
1178 
1179         DE_ASSERT(segment_deltas.size() != 0);
1180 
1181         switch (run.vertex_spacing)
1182         {
1183         case TESSELLATION_SHADER_VERTEX_SPACING_DEFAULT:
1184         case TESSELLATION_SHADER_VERTEX_SPACING_EQUAL:
1185         {
1186             /* For equal vertex spacings, we should end up with a single _tess_coordinate_delta instance
1187              * of a predefined length, describing exactly edge_clamped_rounded_tess_level invocations */
1188             float expected_delta = edge.edge_length / edge_clamped_rounded_tess_level;
1189 
1190             if (segment_deltas.size() != 1)
1191             {
1192                 m_testCtx.getLog() << tcu::TestLog::Message
1193                                    << "More than one segment delta was generated for the following tessellation"
1194                                       " configuration: "
1195                                       "primitive mode:"
1196                                    << primitive_mode_string << "vertex spacing mode:" << vertex_spacing_string
1197                                    << "inner tessellation levels: (" << run.inner[0] << ", " << run.inner[1]
1198                                    << ")"
1199                                       ", outer tessellation levels: ("
1200                                    << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", "
1201                                    << run.outer[3] << ")" << tcu::TestLog::EndMessage;
1202 
1203                 TCU_FAIL("Equal vertex spacing mode tessellation generated segment edges of varying lengths, "
1204                          "whereas only one was expected.");
1205             }
1206 
1207             if (de::abs(segment_deltas[0].delta - expected_delta) > epsilon)
1208             {
1209                 m_testCtx.getLog() << tcu::TestLog::Message << "Invalid segment delta (expected:" << expected_delta
1210                                    << ", found: " << segment_deltas[0].delta
1211                                    << ") was generated for the following tessellation configuration: "
1212                                       "primitive mode:"
1213                                    << primitive_mode_string << "vertex spacing mode:" << vertex_spacing_string
1214                                    << "inner tessellation levels: (" << run.inner[0] << ", " << run.inner[1]
1215                                    << ")"
1216                                       ", outer tessellation levels: ("
1217                                    << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", "
1218                                    << run.outer[3] << ")" << tcu::TestLog::EndMessage;
1219 
1220                 TCU_FAIL("Invalid delta between segments generated by the tessellator configured to run "
1221                          "in equal vertex spacing mode");
1222             }
1223 
1224             if (segment_deltas[0].counter != (unsigned int)edge_clamped_rounded_tess_level)
1225             {
1226                 m_testCtx.getLog() << tcu::TestLog::Message
1227                                    << "Invalid amount of segments (expected:" << (int)edge_clamped_rounded_tess_level
1228                                    << ", found: " << segment_deltas[0].counter
1229                                    << ") "
1230                                       "was generated for the following tessellation configuration: "
1231                                       "primitive mode:"
1232                                    << primitive_mode_string << "vertex spacing mode:" << vertex_spacing_string
1233                                    << "inner tessellation levels: (" << run.inner[0] << ", " << run.inner[1]
1234                                    << ")"
1235                                       ", outer tessellation levels: ("
1236                                    << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", "
1237                                    << run.outer[3] << ")" << tcu::TestLog::EndMessage;
1238 
1239                 TCU_FAIL("Invalid amount of segments generated for equal vertex spacing mode");
1240             }
1241 
1242             break;
1243         } /* default/equal vertex spacing */
1244 
1245         case TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_EVEN:
1246         case TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD:
1247         {
1248             /* For fractional vertex spacings, situation is more tricky. The extension specification
1249              * is very liberal when it comes to defining segment lengths. Hence the only thing we
1250              * should be testing here is that:
1251              *
1252              * a) No more than 2 deltas are generated for a single edge.
1253              *
1254              * b) If a single delta is generated, it should:
1255              *
1256              *    1. define exactly edge_clamped_rounded_tess_level-2 segments if
1257              *    |edge_clamped_rounded_tess_level - edge_clamped_tess_level| == 2.0f,
1258              *
1259              *    2. define exactly edge_clamped_rounded_tess_level segments otherwise.
1260              *
1261              * c) If two deltas are generated, one of them should define 2 segments, and the other
1262              *    one should define edge_clamped_rounded_tess_level-2 segments.
1263              */
1264 
1265             if (segment_deltas.size() > 2)
1266             {
1267                 m_testCtx.getLog() << tcu::TestLog::Message << "More than two segment deltas (" << segment_deltas.size()
1268                                    << ") were generated for the following tessellation configuration: "
1269                                       "primitive mode:"
1270                                    << primitive_mode_string << "vertex spacing mode:" << vertex_spacing_string
1271                                    << "inner tessellation levels: (" << run.inner[0] << ", " << run.inner[1]
1272                                    << ")"
1273                                       ", outer tessellation levels: ("
1274                                    << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", "
1275                                    << run.outer[3] << ")" << tcu::TestLog::EndMessage;
1276 
1277                 TCU_FAIL("Fractional spacing mode tessellated edges to segments of more than two "
1278                          "differentiable lengths");
1279             }
1280 
1281             if (segment_deltas.size() == 1)
1282             {
1283                 int expected_counter = 0;
1284 
1285                 if (run.primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES)
1286                 {
1287                     /* With each triangle level, 2 segments go out. Each triangle consists of 3 edges */
1288                     expected_counter = (int)outermost_edge_tess_level_clamped_rounded - 2 * (n_edge / 3);
1289                 }
1290                 else
1291                 {
1292                     expected_counter = (int)edge_clamped_rounded_tess_level;
1293                     if (run.primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS &&
1294                         run.vertex_spacing == TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD)
1295                     {
1296                         /* 8 edges expected in total; we should expect 2 segments less for the inner quad */
1297                         expected_counter = (int)edge_clamped_rounded_tess_level - 2 * (n_edge / 4);
1298                     }
1299 
1300                     /* For degenerate cases, always assume exactly one segment should be generated */
1301                     if (edge.tess_level <= 0.0f)
1302                     {
1303                         expected_counter = 1;
1304                     }
1305                 }
1306 
1307                 if (segment_deltas[0].counter != (unsigned int)expected_counter)
1308                 {
1309                     m_testCtx.getLog() << tcu::TestLog::Message
1310                                        << "Invalid amount of segments (expected:" << expected_counter
1311                                        << ", found: " << segment_deltas[0].counter
1312                                        << ") "
1313                                           "was generated for the following tessellation configuration: "
1314                                           "primitive mode:"
1315                                        << primitive_mode_string << "vertex spacing mode:" << vertex_spacing_string
1316                                        << "inner tessellation levels: (" << run.inner[0] << ", " << run.inner[1]
1317                                        << ")"
1318                                           ", outer tessellation levels: ("
1319                                        << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", "
1320                                        << run.outer[3] << ")" << tcu::TestLog::EndMessage;
1321 
1322                     TCU_FAIL("Invalid amount of segments generated for fractional vertex spacing mode");
1323                 }
1324             }
1325             else
1326             {
1327                 DE_ASSERT(segment_deltas.size() == 2);
1328 
1329                 if (!((segment_deltas[0].counter == 2 &&
1330                        segment_deltas[1].counter == ((unsigned int)edge_clamped_rounded_tess_level - 2)) ||
1331                       (segment_deltas[1].counter == 2 &&
1332                        segment_deltas[0].counter == ((unsigned int)edge_clamped_rounded_tess_level - 2))))
1333                 {
1334                     m_testCtx.getLog() << tcu::TestLog::Message << "Invalid number of segments with different deltas ("
1335                                        << segment_deltas[0].delta << " and " << segment_deltas[1].delta
1336                                        << ") was generated. "
1337                                           "primitive mode:"
1338                                        << primitive_mode_string << "vertex spacing mode:" << vertex_spacing_string
1339                                        << "inner tessellation levels: (" << run.inner[0] << ", " << run.inner[1]
1340                                        << ")"
1341                                           ", outer tessellation levels: ("
1342                                        << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", "
1343                                        << run.outer[3] << ")" << tcu::TestLog::EndMessage;
1344 
1345                     TCU_FAIL("Equal amount of segments was generated for segments of different deltas");
1346                 }
1347 
1348                 if (segment_deltas[0].counter != 2 && segment_deltas[1].counter != 2)
1349                 {
1350                     m_testCtx.getLog() << tcu::TestLog::Message
1351                                        << "Neither of the segments generated by the tessellator was defined "
1352                                           "exactly twice."
1353                                           "primitive mode:"
1354                                        << primitive_mode_string << "vertex spacing mode:" << vertex_spacing_string
1355                                        << "inner tessellation levels: (" << run.inner[0] << ", " << run.inner[1]
1356                                        << ")"
1357                                           ", outer tessellation levels: ("
1358                                        << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", "
1359                                        << run.outer[3] << ")" << tcu::TestLog::EndMessage;
1360 
1361                     TCU_FAIL("Neither of the generated segments was repeated exactly twice "
1362                              "for fractional vertex spacing mode");
1363                 }
1364             }
1365 
1366             break;
1367         } /* fractional even/odd vertex spacing types */
1368 
1369         default:
1370         {
1371             TCU_FAIL("Unrecognized vertex spacing mode");
1372         }
1373         } /* switch (run.vertex_spacing) */
1374     }     /* for (all edges) */
1375 }
1376 } // namespace glcts
1377 
1378 /* namespace glcts */
1379