blob: fa5badd70b34b6c753f3d668cb69434e3fc15924 [file] [log] [blame]
/*-------------------------------------------------------------------------
* 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[] = { &current_tl_point, &current_tr_point,
&current_bl_point, &current_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 */