| /*------------------------------------------------------------------------- |
| * OpenGL Conformance Test Suite |
| * ----------------------------- |
| * |
| * Copyright (c) 2014-2016 The Khronos Group Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ /*! |
| * \file |
| * \brief |
| */ /*-------------------------------------------------------------------*/ |
| |
| #include "esextcTessellationShaderVertexSpacing.hpp" |
| #include "esextcTessellationShaderUtils.hpp" |
| #include "gluContextInfo.hpp" |
| #include "gluDefs.hpp" |
| #include "glwEnums.hpp" |
| #include "glwFunctions.hpp" |
| #include "tcuTestLog.hpp" |
| #include <algorithm> |
| |
| /* Precision, with which the test should be executed. */ |
| const float epsilon = 1e-3f; |
| |
| namespace glcts |
| { |
| /** Compares two barycentric/cartesian coordinates, using test-wide epsilon. |
| * |
| * @param in Coordinate to compare current instance against. |
| * |
| * @return true if the coordinates are equal, false otherwise. |
| **/ |
| bool TessellationShaderVertexSpacing::_tess_coordinate::operator==( |
| const TessellationShaderVertexSpacing::_tess_coordinate& in) const |
| { |
| if (de::abs(this->u - in.u) < epsilon && de::abs(this->v - in.v) < epsilon && de::abs(this->w - in.w) < epsilon) |
| { |
| return true; |
| } |
| else |
| { |
| return false; |
| } |
| } |
| |
| /** Compares two Cartesian coordinates, using test-wide epsilon. |
| * |
| * @param in Coordinate to compare current instance against. |
| * |
| * @return true if the coordinates are equal, false otherwise. |
| **/ |
| bool TessellationShaderVertexSpacing::_tess_coordinate_cartesian::operator==( |
| const TessellationShaderVertexSpacing::_tess_coordinate_cartesian& in) const |
| { |
| if (de::abs(this->x - in.x) < epsilon && de::abs(this->y - in.y) < epsilon) |
| { |
| return true; |
| } |
| else |
| { |
| return false; |
| } |
| } |
| |
| /** Constructor |
| * |
| * @param context Test context |
| **/ |
| TessellationShaderVertexSpacing::TessellationShaderVertexSpacing(Context& context, const ExtParameters& extParams) |
| : TestCaseBase(context, extParams, "vertex_spacing", "Verifies vertex spacing qualifier behaves as specified") |
| , m_gl_max_tess_gen_level_value(0) |
| , m_vao_id(0) |
| , m_utils(DE_NULL) |
| { |
| /* Left blank on purpose */ |
| } |
| |
| /** Comparator function, used to compare two _tess_coordinate_cartesian |
| * instances by their X components. |
| * |
| * @param a First coordinate to use for comparison; |
| * @param b Second coordinate to use for comparison. |
| * |
| * @return true if X component of @param a is lower than b's; |
| * false otherwise. |
| **/ |
| bool TessellationShaderVertexSpacing::compareEdgeByX(_tess_coordinate_cartesian a, _tess_coordinate_cartesian b) |
| { |
| return a.x < b.x; |
| } |
| |
| /** Comparator function, used to compare two _tess_coordinate_cartesian |
| * instances by their Y components. |
| * |
| * @param a First coordinate to use for comparison; |
| * @param b Second coordinate to use for comparison. |
| * |
| * @return true if Y component of @param a is lower than b's; |
| * false otherwise. |
| **/ |
| bool TessellationShaderVertexSpacing::compareEdgeByY(_tess_coordinate_cartesian a, _tess_coordinate_cartesian b) |
| { |
| return a.y < b.y; |
| } |
| |
| /** Deinitializes ES objects created for the test. */ |
| void TessellationShaderVertexSpacing::deinit() |
| { |
| /* Call base class' deinit() */ |
| TestCaseBase::deinit(); |
| |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| /* Unbind vertex array object */ |
| gl.bindVertexArray(0); |
| |
| /* Delete vertex array object */ |
| if (m_vao_id != 0) |
| { |
| gl.deleteVertexArrays(1, &m_vao_id); |
| |
| m_vao_id = 0; |
| } |
| |
| /* Deinitialize utils instance */ |
| if (m_utils != DE_NULL) |
| { |
| delete m_utils; |
| |
| m_utils = DE_NULL; |
| } |
| } |
| |
| /** Takes data generated by tessellator for a specific run configuration and |
| * converts it into a set of edges that correspond to subsequent isolines. |
| * This function asserts that the run uses a 'isolines' primitive mode. |
| * |
| * @param run Test run properties. |
| * |
| * @return A vector storing found edges. |
| **/ |
| TessellationShaderVertexSpacing::_tess_edges TessellationShaderVertexSpacing::getEdgesForIsolinesTessellation( |
| const _run& run) |
| { |
| _tess_edges result; |
| |
| /* First convert the array data to a vector of edges, where each edge |
| * is a vector of points with the same V component. After this is done, |
| * points for each edge need to be sorted by U component. |
| */ |
| for (unsigned int n_vertex = 0; n_vertex < run.n_vertices; ++n_vertex) |
| { |
| /* Isolines are simple - we only need to create a new edge per each unique height */ |
| const float* coordinate = (const float*)(&run.data[0]) + 3 /* components */ * n_vertex; |
| _tess_coordinate_cartesian new_item; |
| |
| new_item.x = coordinate[0]; |
| new_item.y = coordinate[1]; |
| |
| /* Is V recognized? */ |
| _tess_edges_iterator edges_iterator; |
| |
| for (edges_iterator = result.begin(); edges_iterator != result.end(); edges_iterator++) |
| { |
| _tess_edge& edge = *edges_iterator; |
| |
| /* Each edge uses the same Y component, so we only need to check the first entry */ |
| if (de::abs(edge.points[0].y - coordinate[1]) < epsilon) |
| { |
| /* Add the new point to the vector */ |
| edge.points.push_back(new_item); |
| |
| break; |
| } |
| } /* for (all edges) */ |
| |
| if (edges_iterator == result.end()) |
| { |
| /* New edge starts at this point. |
| * |
| * Note that outermost tessellation level does not apply to this |
| * primitive mode. |
| **/ |
| _tess_edge new_edge(run.outer[1], run.outer[1], 1.0f); |
| |
| new_edge.points.push_back(new_item); |
| |
| result.push_back(new_edge); |
| } |
| } /* for (all vertices) */ |
| |
| /* For each edge, sort the points by U coordinate */ |
| for (_tess_edges_iterator edges_iterator = result.begin(); edges_iterator != result.end(); ++edges_iterator) |
| { |
| _tess_edge_points& edge_points = edges_iterator->points; |
| |
| std::sort(edge_points.begin(), edge_points.end(), compareEdgeByX); |
| } |
| |
| /* Done */ |
| return result; |
| } |
| |
| /** Takes data generated by tessellator for a specific run configuration and |
| * converts it into a set of edges that define the outer and inner quad. |
| * This function asserts that the run uses a 'quads' primitive mode. |
| * |
| * @param run Test run properties. |
| * |
| * @return A vector storing found edges. |
| **/ |
| TessellationShaderVertexSpacing::_tess_edges TessellationShaderVertexSpacing::getEdgesForQuadsTessellation( |
| const _run& run) |
| { |
| _tess_edges result; |
| |
| /* First, convert the raw coordinate array into a vector of cartesian coordinates. |
| * For this method, we will need to take out vertices in no specific order. */ |
| std::vector<_tess_coordinate_cartesian> coordinates; |
| |
| for (unsigned int n_vertex = 0; n_vertex < run.n_vertices; ++n_vertex) |
| { |
| _tess_coordinate_cartesian new_coordinate; |
| const float* vertex_data = (const float*)(&run.data[0]) + 3 /* components */ * n_vertex; |
| |
| new_coordinate.x = vertex_data[0]; |
| new_coordinate.y = vertex_data[1]; |
| |
| coordinates.push_back(new_coordinate); |
| } |
| |
| /* Data set is expected to describe an outer and inner rectangles. We will execute the quad extraction |
| * process in two iterations: |
| * |
| * - first iteration will determine all coordinates that are part of the outer quad; |
| * - second iteration will focus on the inner quad. |
| * |
| * Each iteration will start from identifying corner vertices: |
| * |
| * - top-left corner (x delta from (0.5, 0.5) should be negative, y delta from (0.5, 0.5) should be positive); |
| * - top-right corner (x delta from (0.5, 0.5) should be positive, y delta from (0.5, 0.5) should be positive); |
| * - bottom-left corner (x delta from (0.5, 0.5) should be negative, y delta from (0.5, 0.5) should be negative); |
| * - bottom-right corner (x delta from (0.5, 0.5) should be positive, y delta from (0.5, 0.5) should be negative); |
| * |
| * Once we know where the corner vertices are, we will remove them from the data set and iterate again over the |
| * data set to identify all points that are part of edges connecting the edge corners. After the loop is done, |
| * these vertices will have been associated with relevant edges and removed from the data sets. |
| * |
| * Once two iterations are complete, we're done. |
| */ |
| const unsigned int n_iterations = (run.inner[0] > 1) ? 2 : 1; |
| |
| for (unsigned int n_iteration = 0; n_iteration < n_iterations; ++n_iteration) |
| { |
| _tess_coordinate_cartesian current_tl_point; |
| float current_tl_point_delta = 0.0f; |
| _tess_coordinate_cartesian current_tr_point; |
| float current_tr_point_delta = 0.0f; |
| _tess_coordinate_cartesian current_bl_point; |
| float current_bl_point_delta = 0.0f; |
| _tess_coordinate_cartesian current_br_point; |
| float current_br_point_delta = 0.0f; |
| |
| /* Iterate over all points */ |
| for (std::vector<_tess_coordinate_cartesian>::const_iterator coordinate_iterator = coordinates.begin(); |
| coordinate_iterator != coordinates.end(); coordinate_iterator++) |
| { |
| const _tess_coordinate_cartesian& coordinate = *coordinate_iterator; |
| float delta_x = coordinate.x - 0.5f; |
| float delta_y = coordinate.y - 0.5f; |
| float delta = deFloatSqrt(delta_x * delta_x + delta_y * delta_y); |
| |
| /* top-left corner (x delta from (0.5, 0.5) should be negative, y delta from (0.5, 0.5) should be positive); */ |
| if (delta_x <= 0.0f && delta_y >= 0.0f) |
| { |
| if (delta > current_tl_point_delta) |
| { |
| current_tl_point = coordinate; |
| current_tl_point_delta = delta; |
| } |
| } |
| |
| /* top-right corner (x delta from (0.5, 0.5) should be positive, y delta from (0.5, 0.5) should be positive); */ |
| if (delta_x >= 0.0f && delta_y >= 0.0f) |
| { |
| if (delta > current_tr_point_delta) |
| { |
| current_tr_point = coordinate; |
| current_tr_point_delta = delta; |
| } |
| } |
| |
| /* bottom-left corner (x delta from (0.5, 0.5) should be negative, y delta from (0.5, 0.5) should be negative); */ |
| if (delta_x <= 0.0f && delta_y <= 0.0f) |
| { |
| if (delta > current_bl_point_delta) |
| { |
| current_bl_point = coordinate; |
| current_bl_point_delta = delta; |
| } |
| } |
| |
| /* bottom-right corner (x delta from (0.5, 0.5) should be positive, y delta from (0.5, 0.5) should be negative); */ |
| if (delta_x >= 0.0f && delta_y <= 0.0f) |
| { |
| if (delta > current_br_point_delta) |
| { |
| current_br_point = coordinate; |
| current_br_point_delta = delta; |
| } |
| } |
| } /* for (all coordinates) */ |
| |
| /* Note: If any of the outer tessellation level is 1, at least |
| * two "current" points will refer to the same point. |
| * |
| * Now that we know where the corner vertices are, remove them from the data set |
| */ |
| const _tess_coordinate_cartesian* corner_coordinate_ptrs[] = { ¤t_tl_point, ¤t_tr_point, |
| ¤t_bl_point, ¤t_br_point }; |
| const unsigned int n_corner_coordinate_ptrs = |
| sizeof(corner_coordinate_ptrs) / sizeof(corner_coordinate_ptrs[0]); |
| |
| for (unsigned int n_corner_coordinate = 0; n_corner_coordinate < n_corner_coordinate_ptrs; |
| ++n_corner_coordinate) |
| { |
| const _tess_coordinate_cartesian& corner_coordinate = *(corner_coordinate_ptrs[n_corner_coordinate]); |
| std::vector<_tess_coordinate_cartesian>::iterator iterator = |
| std::find(coordinates.begin(), coordinates.end(), corner_coordinate); |
| |
| /* Iterator can be invalid at this point of any of the corner coordinates |
| * referred more than once to the same point. */ |
| if (iterator != coordinates.end()) |
| { |
| coordinates.erase(iterator); |
| } |
| } /* for (all corner coordinates) */ |
| |
| /* Proceed with identification of coordinates describing the edges. |
| * |
| * Note: for inner quad, we need to subtract 2 segments connecting outer and inner coordinates */ |
| float tl_tr_delta = |
| deFloatSqrt((current_tl_point.x - current_tr_point.x) * (current_tl_point.x - current_tr_point.x) + |
| (current_tl_point.y - current_tr_point.y) * (current_tl_point.y - current_tr_point.y)); |
| float tr_br_delta = |
| deFloatSqrt((current_tr_point.x - current_br_point.x) * (current_tr_point.x - current_br_point.x) + |
| (current_tr_point.y - current_br_point.y) * (current_tr_point.y - current_br_point.y)); |
| float br_bl_delta = |
| deFloatSqrt((current_br_point.x - current_bl_point.x) * (current_br_point.x - current_bl_point.x) + |
| (current_br_point.y - current_bl_point.y) * (current_br_point.y - current_bl_point.y)); |
| float bl_tl_delta = |
| deFloatSqrt((current_bl_point.x - current_tl_point.x) * (current_bl_point.x - current_tl_point.x) + |
| (current_bl_point.y - current_tl_point.y) * (current_bl_point.y - current_tl_point.y)); |
| |
| _tess_edge tl_tr_edge(0, 0, tl_tr_delta); |
| _tess_edge tr_br_edge(0, 0, tr_br_delta); |
| _tess_edge br_bl_edge(0, 0, br_bl_delta); |
| _tess_edge bl_tl_edge(0, 0, bl_tl_delta); |
| |
| tl_tr_edge.outermost_tess_level = run.outer[3]; |
| tr_br_edge.outermost_tess_level = run.outer[2]; |
| br_bl_edge.outermost_tess_level = run.outer[1]; |
| bl_tl_edge.outermost_tess_level = run.outer[0]; |
| |
| if (n_iteration == 0) |
| { |
| tl_tr_edge.tess_level = run.outer[3]; |
| tr_br_edge.tess_level = run.outer[2]; |
| br_bl_edge.tess_level = run.outer[1]; |
| bl_tl_edge.tess_level = run.outer[0]; |
| } |
| else |
| { |
| tl_tr_edge.tess_level = run.inner[0] - 2 /* segments between inner and outer edges */; |
| br_bl_edge.tess_level = run.inner[0] - 2 /* segments between inner and outer edges */; |
| tr_br_edge.tess_level = run.inner[1] - 2 /* segments between inner and outer edges */; |
| bl_tl_edge.tess_level = run.inner[1] - 2 /* segments between inner and outer edges */; |
| } |
| |
| /* Add corner points to edge descriptors. Do *NOT* add the same point twice, as |
| * that will confuse verifyEdges() and cause incorrect failures. |
| */ |
| tl_tr_edge.points.push_back(current_tl_point); |
| |
| if (!(current_tl_point == current_tr_point)) |
| { |
| tl_tr_edge.points.push_back(current_tr_point); |
| } |
| |
| tr_br_edge.points.push_back(current_tr_point); |
| |
| if (!(current_tr_point == current_br_point)) |
| { |
| tr_br_edge.points.push_back(current_br_point); |
| } |
| |
| br_bl_edge.points.push_back(current_br_point); |
| |
| if (!(current_br_point == current_bl_point)) |
| { |
| br_bl_edge.points.push_back(current_bl_point); |
| } |
| |
| bl_tl_edge.points.push_back(current_bl_point); |
| |
| if (!(current_bl_point == current_tl_point)) |
| { |
| bl_tl_edge.points.push_back(current_tl_point); |
| } |
| |
| /* Identify points that lie on any of the edges considered */ |
| _tess_edge* edge_ptrs[] = { &tl_tr_edge, &tr_br_edge, &br_bl_edge, &bl_tl_edge }; |
| const unsigned int n_edge_ptrs = sizeof(edge_ptrs) / sizeof(edge_ptrs[0]); |
| |
| for (unsigned int n_edge = 0; n_edge < n_edge_ptrs; ++n_edge) |
| { |
| _tess_edge& edge = *(edge_ptrs[n_edge]); |
| |
| /* Degenerate edges will only consist of one point, for which the following |
| * code needs not be executed */ |
| if (edge.points.size() > 1) |
| { |
| /* Retrieve edge's start & end points */ |
| _tess_coordinate_cartesian edge_start_point = edge.points[0]; |
| _tess_coordinate_cartesian edge_end_point = edge.points[1]; |
| |
| /* Iterate over the data set */ |
| for (std::vector<_tess_coordinate_cartesian>::const_iterator iterator = coordinates.begin(); |
| iterator != coordinates.end(); iterator++) |
| { |
| const _tess_coordinate_cartesian& coordinate = *iterator; |
| |
| if (isPointOnLine(edge_start_point, edge_end_point, coordinate) && |
| !(edge_start_point == coordinate) && !(edge_end_point == coordinate)) |
| { |
| /* Make sure the point has not already been added. If this happens, |
| * it is very likely there is a bug in the way the implementation's |
| * support of point mode */ |
| if (std::find_if(edge.points.begin(), edge.points.end(), |
| _comparator_exact_tess_coordinate_match(coordinate)) == edge.points.end()) |
| { |
| edge.points.push_back(coordinate); |
| } |
| else |
| { |
| std::string primitive_mode_string = |
| TessellationShaderUtils::getESTokenForPrimitiveMode(run.primitive_mode); |
| std::string vertex_spacing_string = |
| TessellationShaderUtils::getESTokenForVertexSpacingMode(run.vertex_spacing); |
| |
| m_testCtx.getLog() |
| << tcu::TestLog::Message << "A duplicate vertex" |
| << " (" << coordinate.x << ", " << coordinate.y |
| << ") was found in set of coordinates generated for the following configuration:" |
| << " inner tessellation levels:(" << run.inner[0] << ", " << run.inner[1] |
| << ") outer tessellation levels:(" << run.outer[0] << ", " << run.outer[1] << ", " |
| << run.outer[2] << ", " << run.outer[3] << ") primitive mode:" << primitive_mode_string |
| << " vertex spacing mode:" << vertex_spacing_string << " point mode:yes" |
| << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL("A duplicate vertex was found in generated tessellation coordinate set " |
| "when point mode was used"); |
| } |
| } |
| } /* for (all coordinates in the data set) */ |
| |
| /* Sort all points in the edge relative to the start point */ |
| std::sort(edge.points.begin(), edge.points.end(), _comparator_relative_to_base_point(edge_start_point)); |
| } |
| } /* for (all edges) */ |
| |
| /* Remove all coordinates associated to edges from the data set */ |
| for (unsigned int n_edge = 0; n_edge < n_edge_ptrs; ++n_edge) |
| { |
| _tess_edge& edge = *(edge_ptrs[n_edge]); |
| |
| for (std::vector<_tess_coordinate_cartesian>::const_iterator iterator = edge.points.begin(); |
| iterator != edge.points.end(); iterator++) |
| { |
| const _tess_coordinate_cartesian& coordinate = *iterator; |
| std::vector<_tess_coordinate_cartesian>::iterator dataset_iterator = |
| std::find(coordinates.begin(), coordinates.end(), coordinate); |
| |
| if (dataset_iterator != coordinates.end()) |
| { |
| coordinates.erase(dataset_iterator); |
| } |
| } /* for (all edge points) */ |
| } /* for (all edges) */ |
| |
| /* Store the edges */ |
| for (unsigned int n_edge = 0; n_edge < n_edge_ptrs; ++n_edge) |
| { |
| _tess_edge& edge = *(edge_ptrs[n_edge]); |
| |
| result.push_back(edge); |
| } |
| } /* for (both iterations) */ |
| |
| return result; |
| } |
| |
| /** Takes data generated by tessellator for a specific run configuration and |
| * converts it into a set of edges that correspond to subsequent triangles. |
| * This function asserts that the run uses a 'triangles' primitive mode. |
| * |
| * This function throws a TestError, should an error occur or the data is found |
| * to be incorrect. |
| * |
| * @param run Test run properties. |
| * |
| * @return A vector storing found edges. |
| **/ |
| TessellationShaderVertexSpacing::_tess_edges TessellationShaderVertexSpacing::getEdgesForTrianglesTessellation( |
| const _run& run) |
| { |
| DE_ASSERT(run.data_cartesian != DE_NULL); |
| |
| /* Before we proceed, convert the raw arrayed data into a vector. We'll need to take items out |
| * in an undefined order */ |
| std::vector<_tess_coordinate_cartesian> coordinates; |
| |
| for (unsigned int n_vertex = 0; n_vertex < run.n_vertices; ++n_vertex) |
| { |
| const float* vertex_data_cartesian = (const float*)run.data_cartesian + n_vertex * 2; /* components */ |
| |
| _tess_coordinate_cartesian cartesian; |
| |
| cartesian.x = vertex_data_cartesian[0]; |
| cartesian.y = vertex_data_cartesian[1]; |
| |
| coordinates.push_back(cartesian); |
| } |
| |
| /* The function iterates over the data set generated by the tessellator and: |
| * |
| * 1) Finds three corner vertices. These coordinates are considered to correspond to corner vertices |
| * of the outermost triangle. The coordinates are removed from the data set. Note that it is an |
| * error if less than three coordinates are available in the data set at this point. |
| * 2) Iterates over remaining points in the data set and associates them with AB, BC and CA edges. |
| * Each point found to be a part of any of the edges considered is removed from the data set. |
| * 3) If more than 0 coordinates are still available in the data set at this point, implementation |
| * returns to step 1) |
| * |
| * After the implementation runs out of tessellation coordinates, a few sanity checks are executed |
| * and the function returns to the caller. |
| */ |
| float base_tess_level = 0.0f; |
| unsigned int tess_level_delta = 0; |
| _tess_edges result; |
| |
| /* Make sure to follow the cited extension spec language: |
| * |
| * If the inner tessellation level is one and any of the outer tessellation |
| * levels is greater than one, the inner tessellation level is treated as |
| * though it were originally specified as 1+epsilon and will be rounded up to |
| * result in a two- or three-segment subdivision according to the |
| * tessellation spacing. |
| * |
| */ |
| float inner0_round_clamped_value = 0; |
| |
| TessellationShaderUtils::getTessellationLevelAfterVertexSpacing( |
| run.vertex_spacing, run.inner[0], m_gl_max_tess_gen_level_value, DE_NULL, /* out_clamped */ |
| &inner0_round_clamped_value); |
| |
| if (inner0_round_clamped_value == 1.0f && (run.outer[0] > 1.0f || run.outer[1] > 1.0f || run.outer[2] > 1.0f)) |
| { |
| TessellationShaderUtils::getTessellationLevelAfterVertexSpacing( |
| run.vertex_spacing, inner0_round_clamped_value + 1.0f /* epsilon */, m_gl_max_tess_gen_level_value, |
| DE_NULL, /* out_clamped */ |
| &base_tess_level); |
| } |
| else |
| { |
| TessellationShaderUtils::getTessellationLevelAfterVertexSpacing( |
| run.vertex_spacing, run.inner[0], m_gl_max_tess_gen_level_value, DE_NULL, /* out_clamped */ |
| &base_tess_level); |
| } |
| |
| while (coordinates.size() > 0) |
| { |
| /* If we're using an odd tessellation level, it is an error if less than three coordinates are |
| * available at this point. |
| * If it's an even tessellation level, we must have at least three coordinates at hand OR |
| * one, in which case the only coordinate left is the degenerate one. Should that happen, |
| * it's time to leave. */ |
| if ((((int)base_tess_level) % 2 == 0) && (coordinates.size() == 1)) |
| { |
| /* We're left with the degenerate vertex. Leave the loop */ |
| break; |
| } |
| |
| if (coordinates.size() < 3) |
| { |
| TCU_FAIL("Could not extract three corner vertices that would make up a triangle"); |
| } |
| |
| /* Iterate over all vertices left and identify three corner vertices. For the outermost triangle, |
| * these will be represented by (1, 0, 0), (0, 1, 0) and (0, 0, 1) barycentric coordinates, which |
| * correspond to the following coordinates in Euclidean space: (0.5, 0), (1, 1), (0, 1) (as defined |
| * in TessellationShaderUtils::convertCartesianCoordinatesToBarycentric() ). |
| * |
| * The idea here is to identify vertices that are the closest to the ideal coordinates, not necessarily |
| * to find a perfect match. */ |
| unsigned int curr_index = 0; |
| float delta_v1 = 0.0f; /* barycentric:(1, 0, 0) -> Euclidean:(0.5, 0.0) */ |
| float delta_v2 = 0.0f; /* barycentric:(0, 1, 0) -> Euclidean:(1.0, 1.0) */ |
| float delta_v3 = 0.0f; /* barycentric:(0, 0, 1) -> Euclidean:(0.0, 1.0) */ |
| bool is_first_iteration = true; |
| unsigned int selected_v1_index = 0; |
| unsigned int selected_v2_index = 0; |
| unsigned int selected_v3_index = 0; |
| |
| for (std::vector<_tess_coordinate_cartesian>::const_iterator iterator = coordinates.begin(); |
| iterator != coordinates.end(); iterator++, curr_index++) |
| { |
| const _tess_coordinate_cartesian& cartesian_coordinate = *iterator; |
| |
| float curr_delta_v1 = deFloatSqrt((cartesian_coordinate.x - 0.5f) * (cartesian_coordinate.x - 0.5f) + |
| (cartesian_coordinate.y - 0.0f) * (cartesian_coordinate.y - 0.0f)); |
| float curr_delta_v2 = deFloatSqrt((cartesian_coordinate.x - 1.0f) * (cartesian_coordinate.x - 1.0f) + |
| (cartesian_coordinate.y - 1.0f) * (cartesian_coordinate.y - 1.0f)); |
| float curr_delta_v3 = deFloatSqrt((cartesian_coordinate.x - 0.0f) * (cartesian_coordinate.x - 0.0f) + |
| (cartesian_coordinate.y - 1.0f) * (cartesian_coordinate.y - 1.0f)); |
| |
| if (is_first_iteration) |
| { |
| delta_v1 = curr_delta_v1; |
| delta_v2 = curr_delta_v2; |
| delta_v3 = curr_delta_v3; |
| |
| /* No need to update selected vertex indices, since this is the very first iteration */ |
| is_first_iteration = false; |
| } |
| else |
| { |
| if (curr_delta_v1 < delta_v1) |
| { |
| delta_v1 = curr_delta_v1; |
| selected_v1_index = curr_index; |
| } |
| |
| if (curr_delta_v2 < delta_v2) |
| { |
| delta_v2 = curr_delta_v2; |
| selected_v2_index = curr_index; |
| } |
| |
| if (curr_delta_v3 < delta_v3) |
| { |
| delta_v3 = curr_delta_v3; |
| selected_v3_index = curr_index; |
| } |
| } |
| } /* for (all remaining coordinates) */ |
| |
| /* Extract the vertices out of the data set */ |
| _tess_coordinate_cartesian corner_vertices[] = { *(coordinates.begin() + selected_v1_index), |
| *(coordinates.begin() + selected_v2_index), |
| *(coordinates.begin() + selected_v3_index) }; |
| const unsigned int n_corner_vertices = sizeof(corner_vertices) / sizeof(corner_vertices[0]); |
| |
| /* Remove the vertices from the data set */ |
| for (unsigned int n_corner_vertex = 0; n_corner_vertex < n_corner_vertices; ++n_corner_vertex) |
| { |
| const _tess_coordinate_cartesian& vertex = corner_vertices[n_corner_vertex]; |
| std::vector<_tess_coordinate_cartesian>::iterator iterator = |
| std::find(coordinates.begin(), coordinates.end(), vertex); |
| |
| DE_ASSERT(iterator != coordinates.end()); |
| if (iterator != coordinates.end()) |
| { |
| coordinates.erase(iterator); |
| } |
| } /* for (all corner vertices) */ |
| |
| /* Now that we know where the corner vertices are, identify all points that lie |
| * on edges defined by these vertices */ |
| std::vector<_tess_coordinate_cartesian> edge_v1_v2_vertices; |
| std::vector<_tess_coordinate_cartesian> edge_v2_v3_vertices; |
| std::vector<_tess_coordinate_cartesian> edge_v3_v1_vertices; |
| const _tess_coordinate_cartesian& v1 = corner_vertices[0]; |
| const _tess_coordinate_cartesian& v2 = corner_vertices[1]; |
| const _tess_coordinate_cartesian& v3 = corner_vertices[2]; |
| |
| for (std::vector<_tess_coordinate_cartesian>::const_iterator iterator = coordinates.begin(); |
| iterator != coordinates.end(); iterator++, curr_index++) |
| { |
| const _tess_coordinate_cartesian& cartesian_coordinate = *iterator; |
| |
| if (isPointOnLine(v1, v2, cartesian_coordinate)) |
| { |
| edge_v1_v2_vertices.push_back(*iterator); |
| } |
| |
| if (isPointOnLine(v2, v3, cartesian_coordinate)) |
| { |
| edge_v2_v3_vertices.push_back(*iterator); |
| } |
| |
| if (isPointOnLine(v3, v1, cartesian_coordinate)) |
| { |
| edge_v3_v1_vertices.push_back(*iterator); |
| } |
| } /* for (all coordinates in data set) */ |
| |
| /* Now that edge vertices have been identified, remove them from the data set */ |
| const std::vector<_tess_coordinate_cartesian>* edge_ptrs[] = { &edge_v1_v2_vertices, &edge_v2_v3_vertices, |
| &edge_v3_v1_vertices }; |
| const unsigned int n_edge_ptrs = sizeof(edge_ptrs) / sizeof(edge_ptrs[0]); |
| |
| for (unsigned int n_edge_ptr = 0; n_edge_ptr < n_edge_ptrs; ++n_edge_ptr) |
| { |
| const std::vector<_tess_coordinate_cartesian>& edge = *edge_ptrs[n_edge_ptr]; |
| const unsigned int n_edge_vertices = (unsigned int)edge.size(); |
| |
| for (unsigned int n_edge_vertex = 0; n_edge_vertex < n_edge_vertices; ++n_edge_vertex) |
| { |
| const _tess_coordinate_cartesian& coordinate = edge[n_edge_vertex]; |
| std::vector<_tess_coordinate_cartesian>::iterator iterator = |
| std::find(coordinates.begin(), coordinates.end(), coordinate); |
| |
| if (iterator != coordinates.end()) |
| { |
| coordinates.erase(iterator); |
| } |
| } /* for (all edge vertices) */ |
| } /* for (all edges) */ |
| |
| /* Add corner coordinates to our vectors, but only if they are not |
| * already there. |
| */ |
| if (std::find(edge_v1_v2_vertices.begin(), edge_v1_v2_vertices.end(), v1) == edge_v1_v2_vertices.end()) |
| { |
| edge_v1_v2_vertices.push_back(v1); |
| } |
| |
| if (std::find(edge_v1_v2_vertices.begin(), edge_v1_v2_vertices.end(), v2) == edge_v1_v2_vertices.end()) |
| { |
| edge_v1_v2_vertices.push_back(v2); |
| } |
| |
| if (std::find(edge_v2_v3_vertices.begin(), edge_v2_v3_vertices.end(), v2) == edge_v2_v3_vertices.end()) |
| { |
| edge_v2_v3_vertices.push_back(v2); |
| } |
| |
| if (std::find(edge_v2_v3_vertices.begin(), edge_v2_v3_vertices.end(), v3) == edge_v2_v3_vertices.end()) |
| { |
| edge_v2_v3_vertices.push_back(v3); |
| } |
| |
| if (std::find(edge_v3_v1_vertices.begin(), edge_v3_v1_vertices.end(), v3) == edge_v3_v1_vertices.end()) |
| { |
| edge_v3_v1_vertices.push_back(v3); |
| } |
| |
| if (std::find(edge_v3_v1_vertices.begin(), edge_v3_v1_vertices.end(), v1) == edge_v3_v1_vertices.end()) |
| { |
| edge_v3_v1_vertices.push_back(v1); |
| } |
| |
| /* Sort all points relative to corner point */ |
| std::sort(edge_v1_v2_vertices.begin(), edge_v1_v2_vertices.end(), _comparator_relative_to_base_point(v1)); |
| |
| std::sort(edge_v2_v3_vertices.begin(), edge_v2_v3_vertices.end(), _comparator_relative_to_base_point(v2)); |
| |
| std::sort(edge_v3_v1_vertices.begin(), edge_v3_v1_vertices.end(), _comparator_relative_to_base_point(v3)); |
| |
| /* We now have all the data to update the result vector with new edge data */ |
| for (unsigned int n_edge_ptr = 0; n_edge_ptr < n_edge_ptrs; ++n_edge_ptr) |
| { |
| /* Compute tessellation level values for the edge */ |
| glw::GLfloat curr_tess_level = 0.0f; |
| glw::GLfloat outermost_tess_level = 0.0f; |
| |
| if (tess_level_delta == 0) |
| { |
| switch (n_edge_ptr) |
| { |
| /* Assuming: |
| * |
| * v1 = (1, 0, 0) |
| * v2 = (0, 1, 0) |
| * v3 = (0, 0, 1) |
| */ |
| case 0: /* v1->v2 */ |
| { |
| curr_tess_level = run.outer[2]; |
| outermost_tess_level = run.outer[2]; |
| |
| break; |
| } |
| |
| case 1: /* v2->v3 */ |
| { |
| curr_tess_level = run.outer[0]; |
| outermost_tess_level = run.outer[0]; |
| |
| break; |
| } |
| |
| case 2: /* v3->v1 */ |
| { |
| curr_tess_level = run.outer[1]; |
| outermost_tess_level = run.outer[1]; |
| |
| break; |
| } |
| |
| default: |
| { |
| DE_FATAL("Invalid edge index"); |
| } |
| } /* switch (n_edge_ptr) */ |
| } |
| else |
| { |
| curr_tess_level = base_tess_level - (float)tess_level_delta; |
| outermost_tess_level = base_tess_level; |
| } |
| |
| /* Convert internal representation to _tess_edge */ |
| const std::vector<_tess_coordinate_cartesian>& edge = *edge_ptrs[n_edge_ptr]; |
| const _tess_coordinate_cartesian& edge_start_point = *edge.begin(); |
| const _tess_coordinate_cartesian& edge_end_point = *(edge.begin() + (edge.size() - 1)); |
| float edge_length = |
| deFloatSqrt((edge_end_point.x - edge_start_point.x) * (edge_end_point.x - edge_start_point.x) + |
| (edge_end_point.y - edge_start_point.y) * (edge_end_point.y - edge_start_point.y)); |
| _tess_edge result_edge(curr_tess_level, outermost_tess_level, edge_length); |
| |
| for (std::vector<_tess_coordinate_cartesian>::const_iterator edge_point_iterator = edge.begin(); |
| edge_point_iterator != edge.end(); edge_point_iterator++) |
| { |
| const _tess_coordinate_cartesian& edge_point = *edge_point_iterator; |
| |
| result_edge.points.push_back(edge_point); |
| } |
| |
| /* Good to store the edge now */ |
| result.push_back(result_edge); |
| } /* for (all edges) */ |
| |
| /* Moving on with next inner triangle. As per spec, reduce tessellation level by 2 */ |
| tess_level_delta += 2; |
| } /* while (run.n_vertices > 0) */ |
| |
| return result; |
| } |
| |
| /** Tells whether given two-dimensional point is located on a two-dimensional line defined |
| * by two points. |
| * |
| * @param line_v1 First vertex defining the line; |
| * @param line_v2 Second vertex defining the line; |
| * @param point Point to check. |
| * |
| * @return true if the point was determned to be a part of the line, |
| * false otherwise. |
| **/ |
| bool TessellationShaderVertexSpacing::isPointOnLine(const _tess_coordinate_cartesian& line_v1, |
| const _tess_coordinate_cartesian& line_v2, |
| const _tess_coordinate_cartesian& point) |
| |
| { |
| bool result = false; |
| |
| /* Calculate distance from a point to a line passing through two points */ |
| float Dx = line_v1.x - line_v2.x; |
| float Dy = line_v1.y - line_v2.y; |
| float denominator = deFloatSqrt(Dx * Dx + Dy * Dy); |
| float d = de::abs(Dy * point.x - Dx * point.y + line_v1.x * line_v2.y - line_v2.x * line_v1.y) / denominator; |
| |
| if (de::abs(d) < epsilon) |
| { |
| result = true; |
| } |
| |
| return result; |
| } |
| |
| /** Initializes ES objects necessary to run the test. */ |
| void TessellationShaderVertexSpacing::initTest() |
| { |
| /* Skip if required extensions are not supported. */ |
| if (!m_is_tessellation_shader_supported) |
| { |
| throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED); |
| } |
| |
| /* Initialize Utils instance */ |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| m_utils = new TessellationShaderUtils(gl, this); |
| |
| /* Initialize vertex array object */ |
| gl.genVertexArrays(1, &m_vao_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "Could not generate vertex array object"); |
| |
| gl.bindVertexArray(m_vao_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding vertex array object!"); |
| |
| /* Retrieve GL_MAX_TESS_GEN_LEVEL_EXT value */ |
| gl.getIntegerv(m_glExtTokens.MAX_TESS_GEN_LEVEL, &m_gl_max_tess_gen_level_value); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() call failed for GL_MAX_TESS_GEN_LEVEL_EXT pname"); |
| |
| const _tessellation_shader_vertex_spacing vs_modes[] = { TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, |
| TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_EVEN, |
| TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD, |
| TESSELLATION_SHADER_VERTEX_SPACING_DEFAULT }; |
| const unsigned int n_vs_modes = sizeof(vs_modes) / sizeof(vs_modes[0]); |
| |
| const _tessellation_primitive_mode primitive_modes[] = { TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES, |
| TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES, |
| TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS }; |
| const unsigned int n_primitive_modes = sizeof(primitive_modes) / sizeof(primitive_modes[0]); |
| |
| /* Iterate through all primitive modes */ |
| for (unsigned int n_primitive_mode = 0; n_primitive_mode < n_primitive_modes; ++n_primitive_mode) |
| { |
| _tessellation_primitive_mode primitive_mode = primitive_modes[n_primitive_mode]; |
| |
| /* Generate tessellation level set for current primitive mode */ |
| _tessellation_levels_set tess_levels_set; |
| |
| tess_levels_set = TessellationShaderUtils::getTessellationLevelSetForPrimitiveMode( |
| primitive_mode, m_gl_max_tess_gen_level_value, |
| TESSELLATION_LEVEL_SET_FILTER_INNER_AND_OUTER_LEVELS_USE_DIFFERENT_VALUES); |
| |
| /* Iterate through all vertex spacing modes */ |
| for (unsigned int n_vs_mode = 0; n_vs_mode < n_vs_modes; ++n_vs_mode) |
| { |
| _tessellation_shader_vertex_spacing vs_mode = vs_modes[n_vs_mode]; |
| |
| /* Iterate through all tessellation level combinations */ |
| for (_tessellation_levels_set_const_iterator tess_levels_set_iterator = tess_levels_set.begin(); |
| tess_levels_set_iterator != tess_levels_set.end(); tess_levels_set_iterator++) |
| { |
| const _tessellation_levels& tess_levels = *tess_levels_set_iterator; |
| _run run; |
| |
| /* Skip border cases that this test cannot handle */ |
| if (primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS && |
| vs_mode == TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD && |
| (tess_levels.inner[0] <= 1 || tess_levels.inner[1] <= 1)) |
| { |
| continue; |
| } |
| |
| /* Fill run descriptor */ |
| memcpy(run.inner, tess_levels.inner, sizeof(run.inner)); |
| memcpy(run.outer, tess_levels.outer, sizeof(run.outer)); |
| |
| run.primitive_mode = primitive_mode; |
| run.vertex_spacing = vs_mode; |
| |
| /* Retrieve vertex data for both passes */ |
| run.n_vertices = m_utils->getAmountOfVerticesGeneratedByTessellator( |
| run.primitive_mode, run.inner, run.outer, run.vertex_spacing, true); /* is_point_mode_enabled */ |
| |
| if (run.n_vertices == 0) |
| { |
| std::string primitive_mode_string = |
| TessellationShaderUtils::getESTokenForPrimitiveMode(primitive_mode); |
| std::string vs_mode_string = TessellationShaderUtils::getESTokenForVertexSpacingMode(vs_mode); |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << "No vertices were generated by tessellator for: " |
| "inner tess levels:" |
| "[" |
| << run.inner[0] << ", " << run.inner[1] << "]" |
| ", outer tess levels:" |
| "[" |
| << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", " |
| << run.outer[3] << "]" |
| ", primitive mode: " |
| << primitive_mode_string << ", vertex spacing: " << vs_mode_string |
| << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL("Zero vertices were generated by tessellator"); |
| } |
| |
| /* Retrieve the data buffers */ |
| run.data = m_utils->getDataGeneratedByTessellator( |
| run.inner, true, /* is_point_mode_enabled */ |
| run.primitive_mode, TESSELLATION_SHADER_VERTEX_ORDERING_CCW, run.vertex_spacing, run.outer); |
| |
| /* 'triangles' tessellation data is expressed in barycentric coordinates. Before we can |
| * continue, we need to convert the data to Euclidean space */ |
| if (run.primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES) |
| { |
| run.data_cartesian = new float[run.n_vertices * 2 /* components */]; |
| |
| for (unsigned int n_vertex = 0; n_vertex < run.n_vertices; ++n_vertex) |
| { |
| const float* barycentric_vertex_data = |
| (const float*)(&run.data[0]) + n_vertex * 3; /* components */ |
| float* cartesian_vertex_data = (float*)run.data_cartesian + n_vertex * 2; /* components */ |
| |
| TessellationShaderUtils::convertBarycentricCoordinatesToCartesian(barycentric_vertex_data, |
| cartesian_vertex_data); |
| } |
| } /* if (run.primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES) */ |
| else |
| { |
| run.data_cartesian = DE_NULL; |
| } |
| |
| /* Store the run data */ |
| m_runs.push_back(run); |
| } /* for (all tessellation level values ) */ |
| } /* for (all primitive modes) */ |
| } /* for (all vertex spacing modes) */ |
| } |
| |
| /** Executes the test. |
| * |
| * Sets the test result to QP_TEST_RESULT_FAIL if the test failed, QP_TEST_RESULT_PASS otherwise. |
| * |
| * Note the function throws exception should an error occur! |
| * |
| * @return STOP if the test has finished, CONTINUE to indicate iterate() should be called once again. |
| **/ |
| tcu::TestNode::IterateResult TessellationShaderVertexSpacing::iterate(void) |
| { |
| /* Do not execute if required extensions are not supported. */ |
| if (!m_is_tessellation_shader_supported) |
| { |
| throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED); |
| } |
| |
| /* Initialize the test */ |
| initTest(); |
| |
| /* Iterate through all runs */ |
| for (_runs_const_iterator run_iterator = m_runs.begin(); run_iterator != m_runs.end(); run_iterator++) |
| { |
| _tess_edges edges; |
| const _run& run = *run_iterator; |
| |
| switch (run.primitive_mode) |
| { |
| case TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES: |
| { |
| edges = getEdgesForIsolinesTessellation(run); |
| |
| break; |
| } |
| |
| case TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS: |
| { |
| edges = getEdgesForQuadsTessellation(run); |
| |
| break; |
| } |
| |
| case TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES: |
| { |
| edges = getEdgesForTrianglesTessellation(run); |
| |
| break; |
| } |
| |
| default: |
| { |
| TCU_FAIL("Unrecognized primitive mode"); |
| } |
| } /* switch (run.primitive_mode) */ |
| |
| verifyEdges(edges, run); |
| } /* for (all runs) */ |
| |
| /* All done */ |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| return STOP; |
| } |
| |
| /* Verifies that user-provided edges follow the vertex spacing convention, as |
| * defined in the extension specification. |
| * |
| * This function throws a TestError, should an error occur or the data is found |
| * to be incorrect. |
| * |
| * @param edges Data of all edges to run the check against. |
| * @param run Test run properties. |
| **/ |
| void TessellationShaderVertexSpacing::verifyEdges(const _tess_edges& edges, const _run& run) |
| { |
| /* Cache strings that may be used by logging the routines */ |
| const std::string primitive_mode_string = TessellationShaderUtils::getESTokenForPrimitiveMode(run.primitive_mode); |
| const std::string vertex_spacing_string = |
| TessellationShaderUtils::getESTokenForVertexSpacingMode(run.vertex_spacing); |
| |
| /* Iterate through all edges */ |
| unsigned int n_edge = 0; |
| |
| for (_tess_edges_const_iterator edges_iterator = edges.begin(); edges_iterator != edges.end(); |
| edges_iterator++, n_edge++) |
| { |
| const _tess_edge& edge = *edges_iterator; |
| float edge_clamped_tess_level = 0.0f; |
| float edge_clamped_rounded_tess_level = 0.0f; |
| float outermost_edge_tess_level_clamped_rounded = 0.0f; |
| _tess_coordinate_deltas segment_deltas; |
| |
| TessellationShaderUtils::getTessellationLevelAfterVertexSpacing( |
| run.vertex_spacing, edge.outermost_tess_level, m_gl_max_tess_gen_level_value, DE_NULL, /* out_clamped */ |
| &outermost_edge_tess_level_clamped_rounded); |
| |
| /* Retrieve amount of segments the edge should consist of */ |
| TessellationShaderUtils::getTessellationLevelAfterVertexSpacing( |
| run.vertex_spacing, edge.tess_level, m_gl_max_tess_gen_level_value, &edge_clamped_tess_level, |
| &edge_clamped_rounded_tess_level); |
| |
| /* Take two subsequent points if they are available. Vertex spacing has no meaning |
| * in a world of degenerate edges, so skip the check if we have just encountered one. |
| */ |
| const unsigned int n_points = (unsigned int)edge.points.size(); |
| |
| if (n_points < 2) |
| { |
| continue; |
| } |
| |
| /* Compute segment deltas */ |
| for (unsigned int n_base_point = 0; n_base_point < n_points - 1; n_base_point++) |
| { |
| const _tess_coordinate_cartesian& point_a = edge.points[n_base_point + 0]; |
| const _tess_coordinate_cartesian& point_b = edge.points[n_base_point + 1]; |
| |
| /* Calculate the distance between the points */ |
| float distance_nonsqrt = 0.0f; |
| float distance = 0.0f; |
| |
| distance_nonsqrt = |
| (point_a.x - point_b.x) * (point_a.x - point_b.x) + (point_a.y - point_b.y) * (point_a.y - point_b.y); |
| |
| distance = deFloatSqrt(distance_nonsqrt); |
| |
| /* Check if the distance is not already recognized. */ |
| _tess_coordinate_deltas_iterator deltas_iterator; |
| |
| for (deltas_iterator = segment_deltas.begin(); deltas_iterator != segment_deltas.end(); deltas_iterator++) |
| { |
| if (de::abs(deltas_iterator->delta - distance) < epsilon) |
| { |
| /* Increment the counter and leave */ |
| deltas_iterator->counter++; |
| |
| break; |
| } |
| } |
| |
| if (deltas_iterator == segment_deltas.end()) |
| { |
| /* This is the first time we're encountering a segment of this specific length. */ |
| _tess_coordinate_delta new_item; |
| |
| new_item.counter = 1; |
| new_item.delta = distance; |
| |
| segment_deltas.push_back(new_item); |
| } |
| } /* for (all base points) */ |
| |
| DE_ASSERT(segment_deltas.size() != 0); |
| |
| switch (run.vertex_spacing) |
| { |
| case TESSELLATION_SHADER_VERTEX_SPACING_DEFAULT: |
| case TESSELLATION_SHADER_VERTEX_SPACING_EQUAL: |
| { |
| /* For equal vertex spacings, we should end up with a single _tess_coordinate_delta instance |
| * of a predefined length, describing exactly edge_clamped_rounded_tess_level invocations */ |
| float expected_delta = edge.edge_length / edge_clamped_rounded_tess_level; |
| |
| if (segment_deltas.size() != 1) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message |
| << "More than one segment delta was generated for the following tessellation" |
| " configuration: " |
| "primitive mode:" |
| << primitive_mode_string << "vertex spacing mode:" << vertex_spacing_string |
| << "inner tessellation levels: (" << run.inner[0] << ", " << run.inner[1] |
| << ")" |
| ", outer tessellation levels: (" |
| << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", " |
| << run.outer[3] << ")" << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL("Equal vertex spacing mode tessellation generated segment edges of varying lengths, " |
| "whereas only one was expected."); |
| } |
| |
| if (de::abs(segment_deltas[0].delta - expected_delta) > epsilon) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Invalid segment delta (expected:" << expected_delta |
| << ", found: " << segment_deltas[0].delta |
| << ") was generated for the following tessellation configuration: " |
| "primitive mode:" |
| << primitive_mode_string << "vertex spacing mode:" << vertex_spacing_string |
| << "inner tessellation levels: (" << run.inner[0] << ", " << run.inner[1] |
| << ")" |
| ", outer tessellation levels: (" |
| << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", " |
| << run.outer[3] << ")" << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL("Invalid delta between segments generated by the tessellator configured to run " |
| "in equal vertex spacing mode"); |
| } |
| |
| if (segment_deltas[0].counter != (unsigned int)edge_clamped_rounded_tess_level) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message |
| << "Invalid amount of segments (expected:" << (int)edge_clamped_rounded_tess_level |
| << ", found: " << segment_deltas[0].counter |
| << ") " |
| "was generated for the following tessellation configuration: " |
| "primitive mode:" |
| << primitive_mode_string << "vertex spacing mode:" << vertex_spacing_string |
| << "inner tessellation levels: (" << run.inner[0] << ", " << run.inner[1] |
| << ")" |
| ", outer tessellation levels: (" |
| << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", " |
| << run.outer[3] << ")" << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL("Invalid amount of segments generated for equal vertex spacing mode"); |
| } |
| |
| break; |
| } /* default/equal vertex spacing */ |
| |
| case TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_EVEN: |
| case TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD: |
| { |
| /* For fractional vertex spacings, situation is more tricky. The extension specification |
| * is very liberal when it comes to defining segment lengths. Hence the only thing we |
| * should be testing here is that: |
| * |
| * a) No more than 2 deltas are generated for a single edge. |
| * |
| * b) If a single delta is generated, it should: |
| * |
| * 1. define exactly edge_clamped_rounded_tess_level-2 segments if |
| * |edge_clamped_rounded_tess_level - edge_clamped_tess_level| == 2.0f, |
| * |
| * 2. define exactly edge_clamped_rounded_tess_level segments otherwise. |
| * |
| * c) If two deltas are generated, one of them should define 2 segments, and the other |
| * one should define edge_clamped_rounded_tess_level-2 segments. |
| */ |
| |
| if (segment_deltas.size() > 2) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "More than two segment deltas (" << segment_deltas.size() |
| << ") were generated for the following tessellation configuration: " |
| "primitive mode:" |
| << primitive_mode_string << "vertex spacing mode:" << vertex_spacing_string |
| << "inner tessellation levels: (" << run.inner[0] << ", " << run.inner[1] |
| << ")" |
| ", outer tessellation levels: (" |
| << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", " |
| << run.outer[3] << ")" << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL("Fractional spacing mode tessellated edges to segments of more than two " |
| "differentiable lengths"); |
| } |
| |
| if (segment_deltas.size() == 1) |
| { |
| int expected_counter = 0; |
| |
| if (run.primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES) |
| { |
| /* With each triangle level, 2 segments go out. Each triangle consists of 3 edges */ |
| expected_counter = (int)outermost_edge_tess_level_clamped_rounded - 2 * (n_edge / 3); |
| } |
| else |
| { |
| expected_counter = (int)edge_clamped_rounded_tess_level; |
| if (run.primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS && |
| run.vertex_spacing == TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD) |
| { |
| /* 8 edges expected in total; we should expect 2 segments less for the inner quad */ |
| expected_counter = (int)edge_clamped_rounded_tess_level - 2 * (n_edge / 4); |
| } |
| |
| /* For degenerate cases, always assume exactly one segment should be generated */ |
| if (edge.tess_level <= 0.0f) |
| { |
| expected_counter = 1; |
| } |
| } |
| |
| if (segment_deltas[0].counter != (unsigned int)expected_counter) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message |
| << "Invalid amount of segments (expected:" << expected_counter |
| << ", found: " << segment_deltas[0].counter |
| << ") " |
| "was generated for the following tessellation configuration: " |
| "primitive mode:" |
| << primitive_mode_string << "vertex spacing mode:" << vertex_spacing_string |
| << "inner tessellation levels: (" << run.inner[0] << ", " << run.inner[1] |
| << ")" |
| ", outer tessellation levels: (" |
| << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", " |
| << run.outer[3] << ")" << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL("Invalid amount of segments generated for fractional vertex spacing mode"); |
| } |
| } |
| else |
| { |
| DE_ASSERT(segment_deltas.size() == 2); |
| |
| if (!((segment_deltas[0].counter == 2 && |
| segment_deltas[1].counter == ((unsigned int)edge_clamped_rounded_tess_level - 2)) || |
| (segment_deltas[1].counter == 2 && |
| segment_deltas[0].counter == ((unsigned int)edge_clamped_rounded_tess_level - 2)))) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Invalid number of segments with different deltas (" |
| << segment_deltas[0].delta << " and " << segment_deltas[1].delta |
| << ") was generated. " |
| "primitive mode:" |
| << primitive_mode_string << "vertex spacing mode:" << vertex_spacing_string |
| << "inner tessellation levels: (" << run.inner[0] << ", " << run.inner[1] |
| << ")" |
| ", outer tessellation levels: (" |
| << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", " |
| << run.outer[3] << ")" << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL("Equal amount of segments was generated for segments of different deltas"); |
| } |
| |
| if (segment_deltas[0].counter != 2 && segment_deltas[1].counter != 2) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message |
| << "Neither of the segments generated by the tessellator was defined " |
| "exactly twice." |
| "primitive mode:" |
| << primitive_mode_string << "vertex spacing mode:" << vertex_spacing_string |
| << "inner tessellation levels: (" << run.inner[0] << ", " << run.inner[1] |
| << ")" |
| ", outer tessellation levels: (" |
| << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", " |
| << run.outer[3] << ")" << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL("Neither of the generated segments was repeated exactly twice " |
| "for fractional vertex spacing mode"); |
| } |
| } |
| |
| break; |
| } /* fractional even/odd vertex spacing types */ |
| |
| default: |
| { |
| TCU_FAIL("Unrecognized vertex spacing mode"); |
| } |
| } /* switch (run.vertex_spacing) */ |
| } /* for (all edges) */ |
| } |
| } |
| |
| /* namespace glcts */ |