1 /*------------------------------------------------------------------------- 2 * OpenGL Conformance Test Suite 3 * ----------------------------- 4 * 5 * Copyright (c) 2014-2016 The Khronos Group Inc. 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 * 19 */ /*! 20 * \file 21 * \brief 22 */ /*-------------------------------------------------------------------*/ 23 24 #include "esextcTessellationShaderVertexSpacing.hpp" 25 #include "esextcTessellationShaderUtils.hpp" 26 #include "gluContextInfo.hpp" 27 #include "gluDefs.hpp" 28 #include "glwEnums.hpp" 29 #include "glwFunctions.hpp" 30 #include "tcuTestLog.hpp" 31 #include <algorithm> 32 33 /* Precision, with which the test should be executed. */ 34 const float epsilon = 1e-3f; 35 36 namespace glcts 37 { 38 /** Compares two barycentric/cartesian coordinates, using test-wide epsilon. 39 * 40 * @param in Coordinate to compare current instance against. 41 * 42 * @return true if the coordinates are equal, false otherwise. 43 **/ 44 bool TessellationShaderVertexSpacing::_tess_coordinate::operator==( 45 const TessellationShaderVertexSpacing::_tess_coordinate& in) const 46 { 47 if (de::abs(this->u - in.u) < epsilon && de::abs(this->v - in.v) < epsilon && de::abs(this->w - in.w) < epsilon) 48 { 49 return true; 50 } 51 else 52 { 53 return false; 54 } 55 } 56 57 /** Compares two Cartesian coordinates, using test-wide epsilon. 58 * 59 * @param in Coordinate to compare current instance against. 60 * 61 * @return true if the coordinates are equal, false otherwise. 62 **/ 63 bool TessellationShaderVertexSpacing::_tess_coordinate_cartesian::operator==( 64 const TessellationShaderVertexSpacing::_tess_coordinate_cartesian& in) const 65 { 66 if (de::abs(this->x - in.x) < epsilon && de::abs(this->y - in.y) < epsilon) 67 { 68 return true; 69 } 70 else 71 { 72 return false; 73 } 74 } 75 76 /** Constructor 77 * 78 * @param context Test context 79 **/ 80 TessellationShaderVertexSpacing::TessellationShaderVertexSpacing(Context& context, const ExtParameters& extParams) 81 : TestCaseBase(context, extParams, "vertex_spacing", "Verifies vertex spacing qualifier behaves as specified") 82 , m_gl_max_tess_gen_level_value(0) 83 , m_vao_id(0) 84 , m_utils(DE_NULL) 85 { 86 /* Left blank on purpose */ 87 } 88 89 /** Comparator function, used to compare two _tess_coordinate_cartesian 90 * instances by their X components. 91 * 92 * @param a First coordinate to use for comparison; 93 * @param b Second coordinate to use for comparison. 94 * 95 * @return true if X component of @param a is lower than b's; 96 * false otherwise. 97 **/ 98 bool TessellationShaderVertexSpacing::compareEdgeByX(_tess_coordinate_cartesian a, _tess_coordinate_cartesian b) 99 { 100 return a.x < b.x; 101 } 102 103 /** Comparator function, used to compare two _tess_coordinate_cartesian 104 * instances by their Y components. 105 * 106 * @param a First coordinate to use for comparison; 107 * @param b Second coordinate to use for comparison. 108 * 109 * @return true if Y component of @param a is lower than b's; 110 * false otherwise. 111 **/ 112 bool TessellationShaderVertexSpacing::compareEdgeByY(_tess_coordinate_cartesian a, _tess_coordinate_cartesian b) 113 { 114 return a.y < b.y; 115 } 116 117 /** Deinitializes ES objects created for the test. */ 118 void TessellationShaderVertexSpacing::deinit() 119 { 120 /* Call base class' deinit() */ 121 TestCaseBase::deinit(); 122 123 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 124 125 /* Unbind vertex array object */ 126 gl.bindVertexArray(0); 127 128 /* Delete vertex array object */ 129 if (m_vao_id != 0) 130 { 131 gl.deleteVertexArrays(1, &m_vao_id); 132 133 m_vao_id = 0; 134 } 135 136 /* Deinitialize utils instance */ 137 if (m_utils != DE_NULL) 138 { 139 delete m_utils; 140 141 m_utils = DE_NULL; 142 } 143 } 144 145 /** Takes data generated by tessellator for a specific run configuration and 146 * converts it into a set of edges that correspond to subsequent isolines. 147 * This function asserts that the run uses a 'isolines' primitive mode. 148 * 149 * @param run Test run properties. 150 * 151 * @return A vector storing found edges. 152 **/ 153 TessellationShaderVertexSpacing::_tess_edges TessellationShaderVertexSpacing::getEdgesForIsolinesTessellation( 154 const _run& run) 155 { 156 _tess_edges result; 157 158 /* First convert the array data to a vector of edges, where each edge 159 * is a vector of points with the same V component. After this is done, 160 * points for each edge need to be sorted by U component. 161 */ 162 for (unsigned int n_vertex = 0; n_vertex < run.n_vertices; ++n_vertex) 163 { 164 /* Isolines are simple - we only need to create a new edge per each unique height */ 165 const float* coordinate = (const float*)(&run.data[0]) + 3 /* components */ * n_vertex; 166 _tess_coordinate_cartesian new_item; 167 168 new_item.x = coordinate[0]; 169 new_item.y = coordinate[1]; 170 171 /* Is V recognized? */ 172 _tess_edges_iterator edges_iterator; 173 174 for (edges_iterator = result.begin(); edges_iterator != result.end(); edges_iterator++) 175 { 176 _tess_edge& edge = *edges_iterator; 177 178 /* Each edge uses the same Y component, so we only need to check the first entry */ 179 if (de::abs(edge.points[0].y - coordinate[1]) < epsilon) 180 { 181 /* Add the new point to the vector */ 182 edge.points.push_back(new_item); 183 184 break; 185 } 186 } /* for (all edges) */ 187 188 if (edges_iterator == result.end()) 189 { 190 /* New edge starts at this point. 191 * 192 * Note that outermost tessellation level does not apply to this 193 * primitive mode. 194 **/ 195 _tess_edge new_edge(run.outer[1], run.outer[1], 1.0f); 196 197 new_edge.points.push_back(new_item); 198 199 result.push_back(new_edge); 200 } 201 } /* for (all vertices) */ 202 203 /* For each edge, sort the points by U coordinate */ 204 for (_tess_edges_iterator edges_iterator = result.begin(); edges_iterator != result.end(); ++edges_iterator) 205 { 206 _tess_edge_points& edge_points = edges_iterator->points; 207 208 std::sort(edge_points.begin(), edge_points.end(), compareEdgeByX); 209 } 210 211 /* Done */ 212 return result; 213 } 214 215 /** Takes data generated by tessellator for a specific run configuration and 216 * converts it into a set of edges that define the outer and inner quad. 217 * This function asserts that the run uses a 'quads' primitive mode. 218 * 219 * @param run Test run properties. 220 * 221 * @return A vector storing found edges. 222 **/ 223 TessellationShaderVertexSpacing::_tess_edges TessellationShaderVertexSpacing::getEdgesForQuadsTessellation( 224 const _run& run) 225 { 226 _tess_edges result; 227 228 /* First, convert the raw coordinate array into a vector of cartesian coordinates. 229 * For this method, we will need to take out vertices in no specific order. */ 230 std::vector<_tess_coordinate_cartesian> coordinates; 231 232 for (unsigned int n_vertex = 0; n_vertex < run.n_vertices; ++n_vertex) 233 { 234 _tess_coordinate_cartesian new_coordinate; 235 const float* vertex_data = (const float*)(&run.data[0]) + 3 /* components */ * n_vertex; 236 237 new_coordinate.x = vertex_data[0]; 238 new_coordinate.y = vertex_data[1]; 239 240 coordinates.push_back(new_coordinate); 241 } 242 243 /* Data set is expected to describe an outer and inner rectangles. We will execute the quad extraction 244 * process in two iterations: 245 * 246 * - first iteration will determine all coordinates that are part of the outer quad; 247 * - second iteration will focus on the inner quad. 248 * 249 * Each iteration will start from identifying corner vertices: 250 * 251 * - top-left corner (x delta from (0.5, 0.5) should be negative, y delta from (0.5, 0.5) should be positive); 252 * - top-right corner (x delta from (0.5, 0.5) should be positive, y delta from (0.5, 0.5) should be positive); 253 * - bottom-left corner (x delta from (0.5, 0.5) should be negative, y delta from (0.5, 0.5) should be negative); 254 * - bottom-right corner (x delta from (0.5, 0.5) should be positive, y delta from (0.5, 0.5) should be negative); 255 * 256 * Once we know where the corner vertices are, we will remove them from the data set and iterate again over the 257 * data set to identify all points that are part of edges connecting the edge corners. After the loop is done, 258 * these vertices will have been associated with relevant edges and removed from the data sets. 259 * 260 * Once two iterations are complete, we're done. 261 */ 262 const unsigned int n_iterations = (run.inner[0] > 1) ? 2 : 1; 263 264 for (unsigned int n_iteration = 0; n_iteration < n_iterations; ++n_iteration) 265 { 266 _tess_coordinate_cartesian current_tl_point; 267 float current_tl_point_delta = 0.0f; 268 _tess_coordinate_cartesian current_tr_point; 269 float current_tr_point_delta = 0.0f; 270 _tess_coordinate_cartesian current_bl_point; 271 float current_bl_point_delta = 0.0f; 272 _tess_coordinate_cartesian current_br_point; 273 float current_br_point_delta = 0.0f; 274 275 /* Iterate over all points */ 276 for (std::vector<_tess_coordinate_cartesian>::const_iterator coordinate_iterator = coordinates.begin(); 277 coordinate_iterator != coordinates.end(); coordinate_iterator++) 278 { 279 const _tess_coordinate_cartesian& coordinate = *coordinate_iterator; 280 float delta_x = coordinate.x - 0.5f; 281 float delta_y = coordinate.y - 0.5f; 282 float delta = deFloatSqrt(delta_x * delta_x + delta_y * delta_y); 283 284 /* top-left corner (x delta from (0.5, 0.5) should be negative, y delta from (0.5, 0.5) should be positive); */ 285 if (delta_x <= 0.0f && delta_y >= 0.0f) 286 { 287 if (delta > current_tl_point_delta) 288 { 289 current_tl_point = coordinate; 290 current_tl_point_delta = delta; 291 } 292 } 293 294 /* top-right corner (x delta from (0.5, 0.5) should be positive, y delta from (0.5, 0.5) should be positive); */ 295 if (delta_x >= 0.0f && delta_y >= 0.0f) 296 { 297 if (delta > current_tr_point_delta) 298 { 299 current_tr_point = coordinate; 300 current_tr_point_delta = delta; 301 } 302 } 303 304 /* bottom-left corner (x delta from (0.5, 0.5) should be negative, y delta from (0.5, 0.5) should be negative); */ 305 if (delta_x <= 0.0f && delta_y <= 0.0f) 306 { 307 if (delta > current_bl_point_delta) 308 { 309 current_bl_point = coordinate; 310 current_bl_point_delta = delta; 311 } 312 } 313 314 /* bottom-right corner (x delta from (0.5, 0.5) should be positive, y delta from (0.5, 0.5) should be negative); */ 315 if (delta_x >= 0.0f && delta_y <= 0.0f) 316 { 317 if (delta > current_br_point_delta) 318 { 319 current_br_point = coordinate; 320 current_br_point_delta = delta; 321 } 322 } 323 } /* for (all coordinates) */ 324 325 /* Note: If any of the outer tessellation level is 1, at least 326 * two "current" points will refer to the same point. 327 * 328 * Now that we know where the corner vertices are, remove them from the data set 329 */ 330 const _tess_coordinate_cartesian* corner_coordinate_ptrs[] = { ¤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 **/ 525 TessellationShaderVertexSpacing::_tess_edges TessellationShaderVertexSpacing::getEdgesForTrianglesTessellation( 526 const _run& run) 527 { 528 DE_ASSERT(run.data_cartesian != DE_NULL); 529 530 /* Before we proceed, convert the raw arrayed data into a vector. We'll need to take items out 531 * in an undefined order */ 532 std::vector<_tess_coordinate_cartesian> coordinates; 533 534 for (unsigned int n_vertex = 0; n_vertex < run.n_vertices; ++n_vertex) 535 { 536 const float* vertex_data_cartesian = (const float*)run.data_cartesian + n_vertex * 2; /* components */ 537 538 _tess_coordinate_cartesian cartesian; 539 540 cartesian.x = vertex_data_cartesian[0]; 541 cartesian.y = vertex_data_cartesian[1]; 542 543 coordinates.push_back(cartesian); 544 } 545 546 /* The function iterates over the data set generated by the tessellator and: 547 * 548 * 1) Finds three corner vertices. These coordinates are considered to correspond to corner vertices 549 * of the outermost triangle. The coordinates are removed from the data set. Note that it is an 550 * error if less than three coordinates are available in the data set at this point. 551 * 2) Iterates over remaining points in the data set and associates them with AB, BC and CA edges. 552 * Each point found to be a part of any of the edges considered is removed from the data set. 553 * 3) If more than 0 coordinates are still available in the data set at this point, implementation 554 * returns to step 1) 555 * 556 * After the implementation runs out of tessellation coordinates, a few sanity checks are executed 557 * and the function returns to the caller. 558 */ 559 float base_tess_level = 0.0f; 560 unsigned int tess_level_delta = 0; 561 _tess_edges result; 562 563 /* Make sure to follow the cited extension spec language: 564 * 565 * If the inner tessellation level is one and any of the outer tessellation 566 * levels is greater than one, the inner tessellation level is treated as 567 * though it were originally specified as 1+epsilon and will be rounded up to 568 * result in a two- or three-segment subdivision according to the 569 * tessellation spacing. 570 * 571 */ 572 float inner0_round_clamped_value = 0; 573 574 TessellationShaderUtils::getTessellationLevelAfterVertexSpacing( 575 run.vertex_spacing, run.inner[0], m_gl_max_tess_gen_level_value, DE_NULL, /* out_clamped */ 576 &inner0_round_clamped_value); 577 578 if (inner0_round_clamped_value == 1.0f && (run.outer[0] > 1.0f || run.outer[1] > 1.0f || run.outer[2] > 1.0f)) 579 { 580 TessellationShaderUtils::getTessellationLevelAfterVertexSpacing( 581 run.vertex_spacing, inner0_round_clamped_value + 1.0f /* epsilon */, m_gl_max_tess_gen_level_value, 582 DE_NULL, /* out_clamped */ 583 &base_tess_level); 584 } 585 else 586 { 587 TessellationShaderUtils::getTessellationLevelAfterVertexSpacing( 588 run.vertex_spacing, run.inner[0], m_gl_max_tess_gen_level_value, DE_NULL, /* out_clamped */ 589 &base_tess_level); 590 } 591 592 while (coordinates.size() > 0) 593 { 594 /* If we're using an odd tessellation level, it is an error if less than three coordinates are 595 * available at this point. 596 * If it's an even tessellation level, we must have at least three coordinates at hand OR 597 * one, in which case the only coordinate left is the degenerate one. Should that happen, 598 * it's time to leave. */ 599 if ((((int)base_tess_level) % 2 == 0) && (coordinates.size() == 1)) 600 { 601 /* We're left with the degenerate vertex. Leave the loop */ 602 break; 603 } 604 605 if (coordinates.size() < 3) 606 { 607 TCU_FAIL("Could not extract three corner vertices that would make up a triangle"); 608 } 609 610 /* Iterate over all vertices left and identify three corner vertices. For the outermost triangle, 611 * these will be represented by (1, 0, 0), (0, 1, 0) and (0, 0, 1) barycentric coordinates, which 612 * correspond to the following coordinates in Euclidean space: (0.5, 0), (1, 1), (0, 1) (as defined 613 * in TessellationShaderUtils::convertCartesianCoordinatesToBarycentric() ). 614 * 615 * The idea here is to identify vertices that are the closest to the ideal coordinates, not necessarily 616 * to find a perfect match. */ 617 unsigned int curr_index = 0; 618 float delta_v1 = 0.0f; /* barycentric:(1, 0, 0) -> Euclidean:(0.5, 0.0) */ 619 float delta_v2 = 0.0f; /* barycentric:(0, 1, 0) -> Euclidean:(1.0, 1.0) */ 620 float delta_v3 = 0.0f; /* barycentric:(0, 0, 1) -> Euclidean:(0.0, 1.0) */ 621 bool is_first_iteration = true; 622 unsigned int selected_v1_index = 0; 623 unsigned int selected_v2_index = 0; 624 unsigned int selected_v3_index = 0; 625 626 for (std::vector<_tess_coordinate_cartesian>::const_iterator iterator = coordinates.begin(); 627 iterator != coordinates.end(); iterator++, curr_index++) 628 { 629 const _tess_coordinate_cartesian& cartesian_coordinate = *iterator; 630 631 float curr_delta_v1 = deFloatSqrt((cartesian_coordinate.x - 0.5f) * (cartesian_coordinate.x - 0.5f) + 632 (cartesian_coordinate.y - 0.0f) * (cartesian_coordinate.y - 0.0f)); 633 float curr_delta_v2 = deFloatSqrt((cartesian_coordinate.x - 1.0f) * (cartesian_coordinate.x - 1.0f) + 634 (cartesian_coordinate.y - 1.0f) * (cartesian_coordinate.y - 1.0f)); 635 float curr_delta_v3 = deFloatSqrt((cartesian_coordinate.x - 0.0f) * (cartesian_coordinate.x - 0.0f) + 636 (cartesian_coordinate.y - 1.0f) * (cartesian_coordinate.y - 1.0f)); 637 638 if (is_first_iteration) 639 { 640 delta_v1 = curr_delta_v1; 641 delta_v2 = curr_delta_v2; 642 delta_v3 = curr_delta_v3; 643 644 /* No need to update selected vertex indices, since this is the very first iteration */ 645 is_first_iteration = false; 646 } 647 else 648 { 649 if (curr_delta_v1 < delta_v1) 650 { 651 delta_v1 = curr_delta_v1; 652 selected_v1_index = curr_index; 653 } 654 655 if (curr_delta_v2 < delta_v2) 656 { 657 delta_v2 = curr_delta_v2; 658 selected_v2_index = curr_index; 659 } 660 661 if (curr_delta_v3 < delta_v3) 662 { 663 delta_v3 = curr_delta_v3; 664 selected_v3_index = curr_index; 665 } 666 } 667 } /* for (all remaining coordinates) */ 668 669 /* Extract the vertices out of the data set */ 670 _tess_coordinate_cartesian corner_vertices[] = { *(coordinates.begin() + selected_v1_index), 671 *(coordinates.begin() + selected_v2_index), 672 *(coordinates.begin() + selected_v3_index) }; 673 const unsigned int n_corner_vertices = sizeof(corner_vertices) / sizeof(corner_vertices[0]); 674 675 /* Remove the vertices from the data set */ 676 for (unsigned int n_corner_vertex = 0; n_corner_vertex < n_corner_vertices; ++n_corner_vertex) 677 { 678 const _tess_coordinate_cartesian& vertex = corner_vertices[n_corner_vertex]; 679 std::vector<_tess_coordinate_cartesian>::iterator iterator = 680 std::find(coordinates.begin(), coordinates.end(), vertex); 681 682 DE_ASSERT(iterator != coordinates.end()); 683 if (iterator != coordinates.end()) 684 { 685 coordinates.erase(iterator); 686 } 687 } /* for (all corner vertices) */ 688 689 /* Now that we know where the corner vertices are, identify all points that lie 690 * on edges defined by these vertices */ 691 std::vector<_tess_coordinate_cartesian> edge_v1_v2_vertices; 692 std::vector<_tess_coordinate_cartesian> edge_v2_v3_vertices; 693 std::vector<_tess_coordinate_cartesian> edge_v3_v1_vertices; 694 const _tess_coordinate_cartesian& v1 = corner_vertices[0]; 695 const _tess_coordinate_cartesian& v2 = corner_vertices[1]; 696 const _tess_coordinate_cartesian& v3 = corner_vertices[2]; 697 698 for (std::vector<_tess_coordinate_cartesian>::const_iterator iterator = coordinates.begin(); 699 iterator != coordinates.end(); iterator++, curr_index++) 700 { 701 const _tess_coordinate_cartesian& cartesian_coordinate = *iterator; 702 703 if (isPointOnLine(v1, v2, cartesian_coordinate)) 704 { 705 edge_v1_v2_vertices.push_back(*iterator); 706 } 707 708 if (isPointOnLine(v2, v3, cartesian_coordinate)) 709 { 710 edge_v2_v3_vertices.push_back(*iterator); 711 } 712 713 if (isPointOnLine(v3, v1, cartesian_coordinate)) 714 { 715 edge_v3_v1_vertices.push_back(*iterator); 716 } 717 } /* for (all coordinates in data set) */ 718 719 /* Now that edge vertices have been identified, remove them from the data set */ 720 const std::vector<_tess_coordinate_cartesian>* edge_ptrs[] = { &edge_v1_v2_vertices, &edge_v2_v3_vertices, 721 &edge_v3_v1_vertices }; 722 const unsigned int n_edge_ptrs = sizeof(edge_ptrs) / sizeof(edge_ptrs[0]); 723 724 for (unsigned int n_edge_ptr = 0; n_edge_ptr < n_edge_ptrs; ++n_edge_ptr) 725 { 726 const std::vector<_tess_coordinate_cartesian>& edge = *edge_ptrs[n_edge_ptr]; 727 const unsigned int n_edge_vertices = (unsigned int)edge.size(); 728 729 for (unsigned int n_edge_vertex = 0; n_edge_vertex < n_edge_vertices; ++n_edge_vertex) 730 { 731 const _tess_coordinate_cartesian& coordinate = edge[n_edge_vertex]; 732 std::vector<_tess_coordinate_cartesian>::iterator iterator = 733 std::find(coordinates.begin(), coordinates.end(), coordinate); 734 735 if (iterator != coordinates.end()) 736 { 737 coordinates.erase(iterator); 738 } 739 } /* for (all edge vertices) */ 740 } /* for (all edges) */ 741 742 /* Add corner coordinates to our vectors, but only if they are not 743 * already there. 744 */ 745 if (std::find(edge_v1_v2_vertices.begin(), edge_v1_v2_vertices.end(), v1) == edge_v1_v2_vertices.end()) 746 { 747 edge_v1_v2_vertices.push_back(v1); 748 } 749 750 if (std::find(edge_v1_v2_vertices.begin(), edge_v1_v2_vertices.end(), v2) == edge_v1_v2_vertices.end()) 751 { 752 edge_v1_v2_vertices.push_back(v2); 753 } 754 755 if (std::find(edge_v2_v3_vertices.begin(), edge_v2_v3_vertices.end(), v2) == edge_v2_v3_vertices.end()) 756 { 757 edge_v2_v3_vertices.push_back(v2); 758 } 759 760 if (std::find(edge_v2_v3_vertices.begin(), edge_v2_v3_vertices.end(), v3) == edge_v2_v3_vertices.end()) 761 { 762 edge_v2_v3_vertices.push_back(v3); 763 } 764 765 if (std::find(edge_v3_v1_vertices.begin(), edge_v3_v1_vertices.end(), v3) == edge_v3_v1_vertices.end()) 766 { 767 edge_v3_v1_vertices.push_back(v3); 768 } 769 770 if (std::find(edge_v3_v1_vertices.begin(), edge_v3_v1_vertices.end(), v1) == edge_v3_v1_vertices.end()) 771 { 772 edge_v3_v1_vertices.push_back(v1); 773 } 774 775 /* Sort all points relative to corner point */ 776 std::sort(edge_v1_v2_vertices.begin(), edge_v1_v2_vertices.end(), _comparator_relative_to_base_point(v1)); 777 778 std::sort(edge_v2_v3_vertices.begin(), edge_v2_v3_vertices.end(), _comparator_relative_to_base_point(v2)); 779 780 std::sort(edge_v3_v1_vertices.begin(), edge_v3_v1_vertices.end(), _comparator_relative_to_base_point(v3)); 781 782 /* We now have all the data to update the result vector with new edge data */ 783 for (unsigned int n_edge_ptr = 0; n_edge_ptr < n_edge_ptrs; ++n_edge_ptr) 784 { 785 /* Compute tessellation level values for the edge */ 786 glw::GLfloat curr_tess_level = 0.0f; 787 glw::GLfloat outermost_tess_level = 0.0f; 788 789 if (tess_level_delta == 0) 790 { 791 switch (n_edge_ptr) 792 { 793 /* Assuming: 794 * 795 * v1 = (1, 0, 0) 796 * v2 = (0, 1, 0) 797 * v3 = (0, 0, 1) 798 */ 799 case 0: /* v1->v2 */ 800 { 801 curr_tess_level = run.outer[2]; 802 outermost_tess_level = run.outer[2]; 803 804 break; 805 } 806 807 case 1: /* v2->v3 */ 808 { 809 curr_tess_level = run.outer[0]; 810 outermost_tess_level = run.outer[0]; 811 812 break; 813 } 814 815 case 2: /* v3->v1 */ 816 { 817 curr_tess_level = run.outer[1]; 818 outermost_tess_level = run.outer[1]; 819 820 break; 821 } 822 823 default: 824 { 825 DE_FATAL("Invalid edge index"); 826 } 827 } /* switch (n_edge_ptr) */ 828 } 829 else 830 { 831 curr_tess_level = base_tess_level - (float)tess_level_delta; 832 outermost_tess_level = base_tess_level; 833 } 834 835 /* Convert internal representation to _tess_edge */ 836 const std::vector<_tess_coordinate_cartesian>& edge = *edge_ptrs[n_edge_ptr]; 837 const _tess_coordinate_cartesian& edge_start_point = *edge.begin(); 838 const _tess_coordinate_cartesian& edge_end_point = *(edge.begin() + (edge.size() - 1)); 839 float edge_length = 840 deFloatSqrt((edge_end_point.x - edge_start_point.x) * (edge_end_point.x - edge_start_point.x) + 841 (edge_end_point.y - edge_start_point.y) * (edge_end_point.y - edge_start_point.y)); 842 _tess_edge result_edge(curr_tess_level, outermost_tess_level, edge_length); 843 844 for (std::vector<_tess_coordinate_cartesian>::const_iterator edge_point_iterator = edge.begin(); 845 edge_point_iterator != edge.end(); edge_point_iterator++) 846 { 847 const _tess_coordinate_cartesian& edge_point = *edge_point_iterator; 848 849 result_edge.points.push_back(edge_point); 850 } 851 852 /* Good to store the edge now */ 853 result.push_back(result_edge); 854 } /* for (all edges) */ 855 856 /* Moving on with next inner triangle. As per spec, reduce tessellation level by 2 */ 857 tess_level_delta += 2; 858 } /* while (run.n_vertices > 0) */ 859 860 return result; 861 } 862 863 /** Tells whether given two-dimensional point is located on a two-dimensional line defined 864 * by two points. 865 * 866 * @param line_v1 First vertex defining the line; 867 * @param line_v2 Second vertex defining the line; 868 * @param point Point to check. 869 * 870 * @return true if the point was determned to be a part of the line, 871 * false otherwise. 872 **/ 873 bool TessellationShaderVertexSpacing::isPointOnLine(const _tess_coordinate_cartesian& line_v1, 874 const _tess_coordinate_cartesian& line_v2, 875 const _tess_coordinate_cartesian& point) 876 877 { 878 bool result = false; 879 880 /* Calculate distance from a point to a line passing through two points */ 881 float Dx = line_v1.x - line_v2.x; 882 float Dy = line_v1.y - line_v2.y; 883 float denominator = deFloatSqrt(Dx * Dx + Dy * Dy); 884 float d = de::abs(Dy * point.x - Dx * point.y + line_v1.x * line_v2.y - line_v2.x * line_v1.y) / denominator; 885 886 if (de::abs(d) < epsilon) 887 { 888 result = true; 889 } 890 891 return result; 892 } 893 894 /** Initializes ES objects necessary to run the test. */ 895 void TessellationShaderVertexSpacing::initTest() 896 { 897 /* Skip if required extensions are not supported. */ 898 if (!m_is_tessellation_shader_supported) 899 { 900 throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED); 901 } 902 903 /* Initialize Utils instance */ 904 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 905 906 m_utils = new TessellationShaderUtils(gl, this); 907 908 /* Initialize vertex array object */ 909 gl.genVertexArrays(1, &m_vao_id); 910 GLU_EXPECT_NO_ERROR(gl.getError(), "Could not generate vertex array object"); 911 912 gl.bindVertexArray(m_vao_id); 913 GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding vertex array object!"); 914 915 /* Retrieve GL_MAX_TESS_GEN_LEVEL_EXT value */ 916 gl.getIntegerv(m_glExtTokens.MAX_TESS_GEN_LEVEL, &m_gl_max_tess_gen_level_value); 917 GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() call failed for GL_MAX_TESS_GEN_LEVEL_EXT pname"); 918 919 const _tessellation_shader_vertex_spacing vs_modes[] = { TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, 920 TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_EVEN, 921 TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD, 922 TESSELLATION_SHADER_VERTEX_SPACING_DEFAULT }; 923 const unsigned int n_vs_modes = sizeof(vs_modes) / sizeof(vs_modes[0]); 924 925 const _tessellation_primitive_mode primitive_modes[] = { TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES, 926 TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES, 927 TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS }; 928 const unsigned int n_primitive_modes = sizeof(primitive_modes) / sizeof(primitive_modes[0]); 929 930 /* Iterate through all primitive modes */ 931 for (unsigned int n_primitive_mode = 0; n_primitive_mode < n_primitive_modes; ++n_primitive_mode) 932 { 933 _tessellation_primitive_mode primitive_mode = primitive_modes[n_primitive_mode]; 934 935 /* Generate tessellation level set for current primitive mode */ 936 _tessellation_levels_set tess_levels_set; 937 938 tess_levels_set = TessellationShaderUtils::getTessellationLevelSetForPrimitiveMode( 939 primitive_mode, m_gl_max_tess_gen_level_value, 940 TESSELLATION_LEVEL_SET_FILTER_INNER_AND_OUTER_LEVELS_USE_DIFFERENT_VALUES); 941 942 /* Iterate through all vertex spacing modes */ 943 for (unsigned int n_vs_mode = 0; n_vs_mode < n_vs_modes; ++n_vs_mode) 944 { 945 _tessellation_shader_vertex_spacing vs_mode = vs_modes[n_vs_mode]; 946 947 /* Iterate through all tessellation level combinations */ 948 for (_tessellation_levels_set_const_iterator tess_levels_set_iterator = tess_levels_set.begin(); 949 tess_levels_set_iterator != tess_levels_set.end(); tess_levels_set_iterator++) 950 { 951 const _tessellation_levels& tess_levels = *tess_levels_set_iterator; 952 _run run; 953 954 /* Skip border cases that this test cannot handle */ 955 if (primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS && 956 vs_mode == TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD && 957 (tess_levels.inner[0] <= 1 || tess_levels.inner[1] <= 1)) 958 { 959 continue; 960 } 961 962 /* Fill run descriptor */ 963 memcpy(run.inner, tess_levels.inner, sizeof(run.inner)); 964 memcpy(run.outer, tess_levels.outer, sizeof(run.outer)); 965 966 run.primitive_mode = primitive_mode; 967 run.vertex_spacing = vs_mode; 968 969 /* Retrieve vertex data for both passes */ 970 run.n_vertices = m_utils->getAmountOfVerticesGeneratedByTessellator( 971 run.primitive_mode, run.inner, run.outer, run.vertex_spacing, true); /* is_point_mode_enabled */ 972 973 if (run.n_vertices == 0) 974 { 975 std::string primitive_mode_string = 976 TessellationShaderUtils::getESTokenForPrimitiveMode(primitive_mode); 977 std::string vs_mode_string = TessellationShaderUtils::getESTokenForVertexSpacingMode(vs_mode); 978 979 m_testCtx.getLog() << tcu::TestLog::Message << "No vertices were generated by tessellator for: " 980 "inner tess levels:" 981 "[" 982 << run.inner[0] << ", " << run.inner[1] << "]" 983 ", outer tess levels:" 984 "[" 985 << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", " 986 << run.outer[3] << "]" 987 ", primitive mode: " 988 << primitive_mode_string << ", vertex spacing: " << vs_mode_string 989 << tcu::TestLog::EndMessage; 990 991 TCU_FAIL("Zero vertices were generated by tessellator"); 992 } 993 994 /* Retrieve the data buffers */ 995 run.data = m_utils->getDataGeneratedByTessellator( 996 run.inner, true, /* is_point_mode_enabled */ 997 run.primitive_mode, TESSELLATION_SHADER_VERTEX_ORDERING_CCW, run.vertex_spacing, run.outer); 998 999 /* 'triangles' tessellation data is expressed in barycentric coordinates. Before we can 1000 * continue, we need to convert the data to Euclidean space */ 1001 if (run.primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES) 1002 { 1003 run.data_cartesian = new float[run.n_vertices * 2 /* components */]; 1004 1005 for (unsigned int n_vertex = 0; n_vertex < run.n_vertices; ++n_vertex) 1006 { 1007 const float* barycentric_vertex_data = 1008 (const float*)(&run.data[0]) + n_vertex * 3; /* components */ 1009 float* cartesian_vertex_data = (float*)run.data_cartesian + n_vertex * 2; /* components */ 1010 1011 TessellationShaderUtils::convertBarycentricCoordinatesToCartesian(barycentric_vertex_data, 1012 cartesian_vertex_data); 1013 } 1014 } /* if (run.primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES) */ 1015 else 1016 { 1017 run.data_cartesian = DE_NULL; 1018 } 1019 1020 /* Store the run data */ 1021 m_runs.push_back(run); 1022 } /* for (all tessellation level values ) */ 1023 } /* for (all primitive modes) */ 1024 } /* for (all vertex spacing modes) */ 1025 } 1026 1027 /** Executes the test. 1028 * 1029 * Sets the test result to QP_TEST_RESULT_FAIL if the test failed, QP_TEST_RESULT_PASS otherwise. 1030 * 1031 * Note the function throws exception should an error occur! 1032 * 1033 * @return STOP if the test has finished, CONTINUE to indicate iterate() should be called once again. 1034 **/ 1035 tcu::TestNode::IterateResult TessellationShaderVertexSpacing::iterate(void) 1036 { 1037 /* Do not execute if required extensions are not supported. */ 1038 if (!m_is_tessellation_shader_supported) 1039 { 1040 throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED); 1041 } 1042 1043 /* Initialize the test */ 1044 initTest(); 1045 1046 /* Iterate through all runs */ 1047 for (_runs_const_iterator run_iterator = m_runs.begin(); run_iterator != m_runs.end(); run_iterator++) 1048 { 1049 _tess_edges edges; 1050 const _run& run = *run_iterator; 1051 1052 switch (run.primitive_mode) 1053 { 1054 case TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES: 1055 { 1056 edges = getEdgesForIsolinesTessellation(run); 1057 1058 break; 1059 } 1060 1061 case TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS: 1062 { 1063 edges = getEdgesForQuadsTessellation(run); 1064 1065 break; 1066 } 1067 1068 case TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES: 1069 { 1070 edges = getEdgesForTrianglesTessellation(run); 1071 1072 break; 1073 } 1074 1075 default: 1076 { 1077 TCU_FAIL("Unrecognized primitive mode"); 1078 } 1079 } /* switch (run.primitive_mode) */ 1080 1081 verifyEdges(edges, run); 1082 } /* for (all runs) */ 1083 1084 /* All done */ 1085 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 1086 return STOP; 1087 } 1088 1089 /* Verifies that user-provided edges follow the vertex spacing convention, as 1090 * defined in the extension specification. 1091 * 1092 * This function throws a TestError, should an error occur or the data is found 1093 * to be incorrect. 1094 * 1095 * @param edges Data of all edges to run the check against. 1096 * @param run Test run properties. 1097 **/ 1098 void TessellationShaderVertexSpacing::verifyEdges(const _tess_edges& edges, const _run& run) 1099 { 1100 /* Cache strings that may be used by logging the routines */ 1101 const std::string primitive_mode_string = TessellationShaderUtils::getESTokenForPrimitiveMode(run.primitive_mode); 1102 const std::string vertex_spacing_string = 1103 TessellationShaderUtils::getESTokenForVertexSpacingMode(run.vertex_spacing); 1104 1105 /* Iterate through all edges */ 1106 unsigned int n_edge = 0; 1107 1108 for (_tess_edges_const_iterator edges_iterator = edges.begin(); edges_iterator != edges.end(); 1109 edges_iterator++, n_edge++) 1110 { 1111 const _tess_edge& edge = *edges_iterator; 1112 float edge_clamped_tess_level = 0.0f; 1113 float edge_clamped_rounded_tess_level = 0.0f; 1114 float outermost_edge_tess_level_clamped_rounded = 0.0f; 1115 _tess_coordinate_deltas segment_deltas; 1116 1117 TessellationShaderUtils::getTessellationLevelAfterVertexSpacing( 1118 run.vertex_spacing, edge.outermost_tess_level, m_gl_max_tess_gen_level_value, DE_NULL, /* out_clamped */ 1119 &outermost_edge_tess_level_clamped_rounded); 1120 1121 /* Retrieve amount of segments the edge should consist of */ 1122 TessellationShaderUtils::getTessellationLevelAfterVertexSpacing( 1123 run.vertex_spacing, edge.tess_level, m_gl_max_tess_gen_level_value, &edge_clamped_tess_level, 1124 &edge_clamped_rounded_tess_level); 1125 1126 /* Take two subsequent points if they are available. Vertex spacing has no meaning 1127 * in a world of degenerate edges, so skip the check if we have just encountered one. 1128 */ 1129 const unsigned int n_points = (unsigned int)edge.points.size(); 1130 1131 if (n_points < 2) 1132 { 1133 continue; 1134 } 1135 1136 /* Compute segment deltas */ 1137 for (unsigned int n_base_point = 0; n_base_point < n_points - 1; n_base_point++) 1138 { 1139 const _tess_coordinate_cartesian& point_a = edge.points[n_base_point + 0]; 1140 const _tess_coordinate_cartesian& point_b = edge.points[n_base_point + 1]; 1141 1142 /* Calculate the distance between the points */ 1143 float distance_nonsqrt = 0.0f; 1144 float distance = 0.0f; 1145 1146 distance_nonsqrt = 1147 (point_a.x - point_b.x) * (point_a.x - point_b.x) + (point_a.y - point_b.y) * (point_a.y - point_b.y); 1148 1149 distance = deFloatSqrt(distance_nonsqrt); 1150 1151 /* Check if the distance is not already recognized. */ 1152 _tess_coordinate_deltas_iterator deltas_iterator; 1153 1154 for (deltas_iterator = segment_deltas.begin(); deltas_iterator != segment_deltas.end(); deltas_iterator++) 1155 { 1156 if (de::abs(deltas_iterator->delta - distance) < epsilon) 1157 { 1158 /* Increment the counter and leave */ 1159 deltas_iterator->counter++; 1160 1161 break; 1162 } 1163 } 1164 1165 if (deltas_iterator == segment_deltas.end()) 1166 { 1167 /* This is the first time we're encountering a segment of this specific length. */ 1168 _tess_coordinate_delta new_item; 1169 1170 new_item.counter = 1; 1171 new_item.delta = distance; 1172 1173 segment_deltas.push_back(new_item); 1174 } 1175 } /* for (all base points) */ 1176 1177 DE_ASSERT(segment_deltas.size() != 0); 1178 1179 switch (run.vertex_spacing) 1180 { 1181 case TESSELLATION_SHADER_VERTEX_SPACING_DEFAULT: 1182 case TESSELLATION_SHADER_VERTEX_SPACING_EQUAL: 1183 { 1184 /* For equal vertex spacings, we should end up with a single _tess_coordinate_delta instance 1185 * of a predefined length, describing exactly edge_clamped_rounded_tess_level invocations */ 1186 float expected_delta = edge.edge_length / edge_clamped_rounded_tess_level; 1187 1188 if (segment_deltas.size() != 1) 1189 { 1190 m_testCtx.getLog() << tcu::TestLog::Message 1191 << "More than one segment delta was generated for the following tessellation" 1192 " configuration: " 1193 "primitive mode:" 1194 << primitive_mode_string << "vertex spacing mode:" << vertex_spacing_string 1195 << "inner tessellation levels: (" << run.inner[0] << ", " << run.inner[1] 1196 << ")" 1197 ", outer tessellation levels: (" 1198 << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", " 1199 << run.outer[3] << ")" << tcu::TestLog::EndMessage; 1200 1201 TCU_FAIL("Equal vertex spacing mode tessellation generated segment edges of varying lengths, " 1202 "whereas only one was expected."); 1203 } 1204 1205 if (de::abs(segment_deltas[0].delta - expected_delta) > epsilon) 1206 { 1207 m_testCtx.getLog() << tcu::TestLog::Message << "Invalid segment delta (expected:" << expected_delta 1208 << ", found: " << segment_deltas[0].delta 1209 << ") was generated for the following tessellation configuration: " 1210 "primitive mode:" 1211 << primitive_mode_string << "vertex spacing mode:" << vertex_spacing_string 1212 << "inner tessellation levels: (" << run.inner[0] << ", " << run.inner[1] 1213 << ")" 1214 ", outer tessellation levels: (" 1215 << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", " 1216 << run.outer[3] << ")" << tcu::TestLog::EndMessage; 1217 1218 TCU_FAIL("Invalid delta between segments generated by the tessellator configured to run " 1219 "in equal vertex spacing mode"); 1220 } 1221 1222 if (segment_deltas[0].counter != (unsigned int)edge_clamped_rounded_tess_level) 1223 { 1224 m_testCtx.getLog() << tcu::TestLog::Message 1225 << "Invalid amount of segments (expected:" << (int)edge_clamped_rounded_tess_level 1226 << ", found: " << segment_deltas[0].counter 1227 << ") " 1228 "was generated for the following tessellation configuration: " 1229 "primitive mode:" 1230 << primitive_mode_string << "vertex spacing mode:" << vertex_spacing_string 1231 << "inner tessellation levels: (" << run.inner[0] << ", " << run.inner[1] 1232 << ")" 1233 ", outer tessellation levels: (" 1234 << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", " 1235 << run.outer[3] << ")" << tcu::TestLog::EndMessage; 1236 1237 TCU_FAIL("Invalid amount of segments generated for equal vertex spacing mode"); 1238 } 1239 1240 break; 1241 } /* default/equal vertex spacing */ 1242 1243 case TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_EVEN: 1244 case TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD: 1245 { 1246 /* For fractional vertex spacings, situation is more tricky. The extension specification 1247 * is very liberal when it comes to defining segment lengths. Hence the only thing we 1248 * should be testing here is that: 1249 * 1250 * a) No more than 2 deltas are generated for a single edge. 1251 * 1252 * b) If a single delta is generated, it should: 1253 * 1254 * 1. define exactly edge_clamped_rounded_tess_level-2 segments if 1255 * |edge_clamped_rounded_tess_level - edge_clamped_tess_level| == 2.0f, 1256 * 1257 * 2. define exactly edge_clamped_rounded_tess_level segments otherwise. 1258 * 1259 * c) If two deltas are generated, one of them should define 2 segments, and the other 1260 * one should define edge_clamped_rounded_tess_level-2 segments. 1261 */ 1262 1263 if (segment_deltas.size() > 2) 1264 { 1265 m_testCtx.getLog() << tcu::TestLog::Message << "More than two segment deltas (" << segment_deltas.size() 1266 << ") were generated for the following tessellation configuration: " 1267 "primitive mode:" 1268 << primitive_mode_string << "vertex spacing mode:" << vertex_spacing_string 1269 << "inner tessellation levels: (" << run.inner[0] << ", " << run.inner[1] 1270 << ")" 1271 ", outer tessellation levels: (" 1272 << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", " 1273 << run.outer[3] << ")" << tcu::TestLog::EndMessage; 1274 1275 TCU_FAIL("Fractional spacing mode tessellated edges to segments of more than two " 1276 "differentiable lengths"); 1277 } 1278 1279 if (segment_deltas.size() == 1) 1280 { 1281 int expected_counter = 0; 1282 1283 if (run.primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES) 1284 { 1285 /* With each triangle level, 2 segments go out. Each triangle consists of 3 edges */ 1286 expected_counter = (int)outermost_edge_tess_level_clamped_rounded - 2 * (n_edge / 3); 1287 } 1288 else 1289 { 1290 expected_counter = (int)edge_clamped_rounded_tess_level; 1291 if (run.primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS && 1292 run.vertex_spacing == TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD) 1293 { 1294 /* 8 edges expected in total; we should expect 2 segments less for the inner quad */ 1295 expected_counter = (int)edge_clamped_rounded_tess_level - 2 * (n_edge / 4); 1296 } 1297 1298 /* For degenerate cases, always assume exactly one segment should be generated */ 1299 if (edge.tess_level <= 0.0f) 1300 { 1301 expected_counter = 1; 1302 } 1303 } 1304 1305 if (segment_deltas[0].counter != (unsigned int)expected_counter) 1306 { 1307 m_testCtx.getLog() << tcu::TestLog::Message 1308 << "Invalid amount of segments (expected:" << expected_counter 1309 << ", found: " << segment_deltas[0].counter 1310 << ") " 1311 "was generated for the following tessellation configuration: " 1312 "primitive mode:" 1313 << primitive_mode_string << "vertex spacing mode:" << vertex_spacing_string 1314 << "inner tessellation levels: (" << run.inner[0] << ", " << run.inner[1] 1315 << ")" 1316 ", outer tessellation levels: (" 1317 << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", " 1318 << run.outer[3] << ")" << tcu::TestLog::EndMessage; 1319 1320 TCU_FAIL("Invalid amount of segments generated for fractional vertex spacing mode"); 1321 } 1322 } 1323 else 1324 { 1325 DE_ASSERT(segment_deltas.size() == 2); 1326 1327 if (!((segment_deltas[0].counter == 2 && 1328 segment_deltas[1].counter == ((unsigned int)edge_clamped_rounded_tess_level - 2)) || 1329 (segment_deltas[1].counter == 2 && 1330 segment_deltas[0].counter == ((unsigned int)edge_clamped_rounded_tess_level - 2)))) 1331 { 1332 m_testCtx.getLog() << tcu::TestLog::Message << "Invalid number of segments with different deltas (" 1333 << segment_deltas[0].delta << " and " << segment_deltas[1].delta 1334 << ") was generated. " 1335 "primitive mode:" 1336 << primitive_mode_string << "vertex spacing mode:" << vertex_spacing_string 1337 << "inner tessellation levels: (" << run.inner[0] << ", " << run.inner[1] 1338 << ")" 1339 ", outer tessellation levels: (" 1340 << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", " 1341 << run.outer[3] << ")" << tcu::TestLog::EndMessage; 1342 1343 TCU_FAIL("Equal amount of segments was generated for segments of different deltas"); 1344 } 1345 1346 if (segment_deltas[0].counter != 2 && segment_deltas[1].counter != 2) 1347 { 1348 m_testCtx.getLog() << tcu::TestLog::Message 1349 << "Neither of the segments generated by the tessellator was defined " 1350 "exactly twice." 1351 "primitive mode:" 1352 << primitive_mode_string << "vertex spacing mode:" << vertex_spacing_string 1353 << "inner tessellation levels: (" << run.inner[0] << ", " << run.inner[1] 1354 << ")" 1355 ", outer tessellation levels: (" 1356 << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", " 1357 << run.outer[3] << ")" << tcu::TestLog::EndMessage; 1358 1359 TCU_FAIL("Neither of the generated segments was repeated exactly twice " 1360 "for fractional vertex spacing mode"); 1361 } 1362 } 1363 1364 break; 1365 } /* fractional even/odd vertex spacing types */ 1366 1367 default: 1368 { 1369 TCU_FAIL("Unrecognized vertex spacing mode"); 1370 } 1371 } /* switch (run.vertex_spacing) */ 1372 } /* for (all edges) */ 1373 } 1374 } 1375 1376 /* namespace glcts */ 1377