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