blob: aeaa5c3fc5ef5a23e7a44bfd22f4d9e8c4bd512a [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 "esextcTessellationShaderUtils.hpp"
#include "deMath.h"
#include "glwEnums.hpp"
#include "glwFunctions.hpp"
#include "tcuTestLog.hpp"
#include <sstream>
namespace glcts
{
/** Constructor
*
* @param gl DEQP container for ES entry-points
* @param parentTest Pointer to owning test instance.
**/
TessellationShaderUtils::TessellationShaderUtils(const glw::Functions& gl, glcts::TestCaseBase* parentTest)
: m_gl(gl), m_bo_id(0), m_fs_id(0), m_qo_pg_id(0), m_vs_id(0), m_parent_test(parentTest)
{
init();
}
/** Destructor */
TessellationShaderUtils::~TessellationShaderUtils()
{
deinit();
}
/** Captures data generated by the tessellator when a geometry is drawn
* for user-provided vertex counter program.
*
* @param program Vertex counter program to use.
**/
void TessellationShaderUtils::captureTessellationData(_tessellation_vertex_counter_program& program)
{
/* Cache current program object ID before we continue */
glw::GLint current_po_id = 0;
m_gl.getIntegerv(GL_CURRENT_PROGRAM, &current_po_id);
GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGetIntegerv() failed for GL_CURRENT_PROGRAM pname");
/* Cache current GL_PATCH_VERTICES_EXT setting before continuing */
glw::GLint current_patch_vertices = 0;
m_gl.getIntegerv(GL_PATCH_VERTICES, &current_patch_vertices);
GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGetIntegerv() failed for GL_PATCH_VERTICES_EXT pname");
/* Activate the program object and the query object */
m_gl.useProgram(program.po_id);
GLU_EXPECT_NO_ERROR(m_gl.getError(), "glUseProgram() failed");
m_gl.beginQuery(m_parent_test->m_glExtTokens.PRIMITIVES_GENERATED, m_qo_pg_id);
GLU_EXPECT_NO_ERROR(m_gl.getError(), "glBeginQuery() called for target GL_PRIMITIVES_GENERATED_EXT failed");
/* Disable rasterization, if it's enabled */
glw::GLboolean is_rasterization_disabled = m_gl.isEnabled(GL_RASTERIZER_DISCARD);
GLU_EXPECT_NO_ERROR(m_gl.getError(), "glIsEnabled(GL_RASTERIZER_DISCARD) failed");
if (is_rasterization_disabled)
{
m_gl.enable(GL_RASTERIZER_DISCARD);
GLU_EXPECT_NO_ERROR(m_gl.getError(), "glEnable(GL_RASTERIZER_DISCARD) failed");
}
/* Update GL_PATCH_VERTICES_EXT */
m_gl.patchParameteri(m_parent_test->m_glExtTokens.PATCH_VERTICES, program.n_patch_vertices);
GLU_EXPECT_NO_ERROR(m_gl.getError(), "glPatchParameteriEXT() failed");
/* Draw the test geometry */
m_gl.drawArrays(m_parent_test->m_glExtTokens.PATCHES, 0, /* first */
program.n_patch_vertices); /* count */
GLU_EXPECT_NO_ERROR(m_gl.getError(), "glDrawArrays() failed");
/* End the query and retrieve the result */
glw::GLuint queryValue = 0;
m_gl.endQuery(m_parent_test->m_glExtTokens.PRIMITIVES_GENERATED);
GLU_EXPECT_NO_ERROR(m_gl.getError(), "glEndQuery(GL_PRIMITIVES_GENERATED_EXT) failed");
m_gl.getQueryObjectuiv(m_qo_pg_id, GL_QUERY_RESULT, &queryValue);
GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGetQueryiv() failed");
/* Store amount of primitives under result */
program.n_data_vertices = ((unsigned int)queryValue);
if (!program.is_point_mode_enabled)
{
/* Quads get tessellated into triangles, meaning our primitives counter tells how
* many triangles were generated for both triangles and quads; isolines get
* tessellated into line segments.
*/
if (program.primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS ||
program.primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES)
{
program.n_data_vertices *= 3; /* each triangle gets 3 vertices */
}
else
{
program.n_data_vertices *= 2; /* each isoline gets 2 vertices */
}
} /* if (!is_point_mode_enabled) */
if (program.n_data_vertices != 0)
{
/* Now that we now, how many vertices we need to allocate space for, set up TF */
glw::GLint bo_size = static_cast<glw::GLint>(sizeof(float) * 3 /* components */ * program.n_data_vertices);
m_gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_bo_id);
GLU_EXPECT_NO_ERROR(m_gl.getError(), "glBindBuffer() failed");
m_gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* index */, m_bo_id);
GLU_EXPECT_NO_ERROR(m_gl.getError(), "glBindBufferBase() failed");
m_gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, bo_size, DE_NULL, /* data */
GL_STATIC_DRAW);
GLU_EXPECT_NO_ERROR(m_gl.getError(), "glBufferData() failed");
/* Set up TF */
glw::GLenum tf_mode =
TessellationShaderUtils::getTFModeForPrimitiveMode(program.primitive_mode, program.is_point_mode_enabled);
m_gl.beginTransformFeedback(tf_mode);
GLU_EXPECT_NO_ERROR(m_gl.getError(), "glBeginTransformFeedback() failed");
m_gl.drawArrays(m_parent_test->m_glExtTokens.PATCHES, 0, /* first */
program.n_patch_vertices); /* count */
GLU_EXPECT_NO_ERROR(m_gl.getError(), "glDrawArrays() failed");
m_gl.endTransformFeedback();
GLU_EXPECT_NO_ERROR(m_gl.getError(), "glEndTransformFeedback() failed");
/* Map the BO and copy the contents */
const void* xfb_data = m_gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, /* offset */
bo_size, GL_MAP_READ_BIT);
GLU_EXPECT_NO_ERROR(m_gl.getError(), "glMapBufferRange() failed");
program.m_data.resize(bo_size);
memcpy(&program.m_data[0], xfb_data, bo_size);
m_gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
GLU_EXPECT_NO_ERROR(m_gl.getError(), "glUnmapBuffer() failed");
} /* if (program.n_data_vertices != 0) */
/* Bring the rasterization back up, if it was enabled prior to this call */
if (!is_rasterization_disabled)
{
m_gl.disable(GL_RASTERIZER_DISCARD);
GLU_EXPECT_NO_ERROR(m_gl.getError(), "glDisable(GL_RASTERIZER_DISCARD) failed");
}
/* Activate the pre-call program object*/
m_gl.useProgram(current_po_id);
GLU_EXPECT_NO_ERROR(m_gl.getError(), "glUseProgram() failed");
/* Bring back pre-call GL_PATCH_VERTICES_EXT setting */
m_gl.patchParameteri(m_parent_test->m_glExtTokens.PATCH_VERTICES, current_patch_vertices);
GLU_EXPECT_NO_ERROR(m_gl.getError(), "glPatchParameteriEXT() failed");
}
/** Compiles all requested shaders. Should any of the shaders not compile,
* TestError exception can be thrown if @param should_succeed is set to true.
*
* @param n_shaders Amount of shader IDs passed in @param shaders argument.
* @param shaders IDs of shader objects to compile.
* @param should_succeed True if the shaders are expected to compile, false if
* it's fine for them to not to compile successfully.
**/
void TessellationShaderUtils::compileShaders(glw::GLint n_shaders, const glw::GLuint* shaders, bool should_succeed)
{
for (glw::GLint n_shader = 0; n_shader < n_shaders; ++n_shader)
{
glw::GLuint shader = shaders[n_shader];
if (shader != 0)
{
glw::GLint compile_status = GL_FALSE;
m_gl.compileShader(shader);
GLU_EXPECT_NO_ERROR(m_gl.getError(), "glCompileShader() failed");
m_gl.getShaderiv(shader, GL_COMPILE_STATUS, &compile_status);
GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGetShaderiv() failed");
if (should_succeed && compile_status != GL_TRUE)
{
std::string info_log = m_parent_test->getCompilationInfoLog(shader);
std::string shader_source = m_parent_test->getShaderSource(shader);
m_parent_test->m_context.getTestContext().getLog() << tcu::TestLog::Message
<< "Compilation failure:\n\n"
<< info_log << "\n\n"
<< "Source:\n\n"
<< shader_source << "\n\n"
<< tcu::TestLog::EndMessage;
TCU_FAIL("Shader compilation failed");
}
else if (!should_succeed && compile_status == GL_TRUE)
{
std::string shader_source = m_parent_test->getShaderSource(shader);
m_parent_test->m_context.getTestContext().getLog() << tcu::TestLog::Message
<< "Compilation failure expected.\nSource:\n\n"
<< shader_source << "\n\n"
<< tcu::TestLog::EndMessage;
TCU_FAIL("Shader compiled successfully, even though it was "
"expected to fail.");
}
}
} /* for (all shaders) */
}
/** Converts input barycentric coordinates to Cartesian coordinate system. The function assumes
* a triangle basis built of the following verticeS: (0.5, 0), (1, 1), (0, 1).
*
* @param barycentric_coordinates Three FP values storing barycentric coordinates of a point. Must
* NOT be NULL.
* @param out_cartesian_coordinates Deref will be used to store two result FP values. Must not be NULL.
**/
void TessellationShaderUtils::convertBarycentricCoordinatesToCartesian(const float* barycentric_coordinates,
float* out_cartesian_coordinates)
{
/* Assume output triangle uses the following base:
*
* (0.5, 0)
* (1, 1)
* (0, 1)
*/
const float triangle_vertex1_cartesian[2] = { 0.5f, 0.0f };
const float triangle_vertex2_cartesian[2] = { 1.0f, 1.0f };
const float triangle_vertex3_cartesian[2] = { 0.0f, 1.0f };
out_cartesian_coordinates[0] = (float)((double)barycentric_coordinates[0] * (double)triangle_vertex1_cartesian[0] +
(double)barycentric_coordinates[1] * (double)triangle_vertex2_cartesian[0] +
(double)barycentric_coordinates[2] * (double)triangle_vertex3_cartesian[0]);
out_cartesian_coordinates[1] = barycentric_coordinates[0] * triangle_vertex1_cartesian[1] +
barycentric_coordinates[1] * triangle_vertex2_cartesian[1] +
barycentric_coordinates[2] * triangle_vertex3_cartesian[1];
}
/** Converts input Cartesian coordinates to barycentric coordinate system. The function assumes
* a triangle basis built of the following verticeS: (0.5, 0), (1, 1), (0, 1).
*
* @param cartesian_coordinates Two FP values storing Cartesian coordinates of a point. Must NOT
* be NULL.
* @param out_barycentric_coordinates Deref will be used to store three result FP values. Must NOT be NULL.
**/
void TessellationShaderUtils::convertCartesianCoordinatesToBarycentric(const float* cartesian_coordinates,
float* out_barycentric_coordinates)
{
/* Assume input triangle uses the following base:
*
* (0.5, 0)
* (1, 1)
* (0, 1)
*/
const float x1 = 0.5f;
const float x2 = 1.0f;
const float x3 = 0.0f;
const float y1 = 0.0f;
const float y2 = 1.0f;
const float y3 = 1.0f;
out_barycentric_coordinates[0] =
((y2 - y3) * (cartesian_coordinates[0] - x3) + (x3 - x2) * (cartesian_coordinates[1] - y3)) /
((y2 - y3) * (x1 - x3) + (x3 - x2) * (y1 - y3));
out_barycentric_coordinates[1] =
((y3 - y1) * (cartesian_coordinates[0] - x3) + (x1 - x3) * (cartesian_coordinates[1] - y3)) /
((y2 - y3) * (x1 - x3) + (x3 - x2) * (y1 - y3));
out_barycentric_coordinates[2] = 1.0f - out_barycentric_coordinates[0] - out_barycentric_coordinates[1];
}
/** Deinitializes ES objects created for TessellationShaderUtils
* instance.
**/
void TessellationShaderUtils::deinit()
{
if (!m_parent_test->m_is_tessellation_shader_supported)
{
return;
}
if (m_bo_id != 0)
{
m_gl.deleteBuffers(1, &m_bo_id);
m_bo_id = 0;
}
if (m_fs_id != 0)
{
m_gl.deleteShader(m_fs_id);
m_fs_id = 0;
}
if (m_qo_pg_id != 0)
{
m_gl.deleteQueries(1, &m_qo_pg_id);
m_qo_pg_id = 0;
}
if (m_vs_id != 0)
{
m_gl.deleteShader(m_vs_id);
m_vs_id = 0;
}
/* Revert TF buffer object bindings */
m_gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* buffer */);
m_gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* index */, 0 /* buffer */);
/* Disable GL_RASTERIZER_DISCARD mode */
m_gl.disable(GL_RASTERIZER_DISCARD);
/* Restore GL_PATCH_VERTICES_EXT value */
m_gl.patchParameteri(m_parent_test->m_glExtTokens.PATCH_VERTICES, 3);
}
/** Retrieves generic tessellation control shader source code.
* The shader users user-specified amount of output patch vertices and:
*
* - sets gl_Position to gl_in[0].gl_Position if second argument is set to false.
* - sets gl_Position to gl_in[gl_InvocationID].gl_Position otherwise.
*
* @param n_patch_vertices Amount of output patch vertices
* to use in the shader.
* @param should_use_glInvocationID_indexed_input See above.
*
* @return Requested string.
*/
std::string TessellationShaderUtils::getGenericTCCode(unsigned int n_patch_vertices,
bool should_use_glInvocationID_indexed_input)
{
std::string result;
const char* tc_body_false = "${VERSION}\n"
"\n"
"${TESSELLATION_SHADER_REQUIRE}\n"
"\n"
"layout (vertices = MAX_VERTICES) out;\n"
"\n"
"uniform vec2 inner_tess_level;\n"
"uniform vec4 outer_tess_level;\n"
"\n"
"void main()\n"
"{\n"
" gl_out[gl_InvocationID].gl_Position = gl_in[0].gl_Position;\n"
"\n"
" gl_TessLevelInner[0] = inner_tess_level.x;\n"
" gl_TessLevelInner[1] = inner_tess_level.y;\n"
" gl_TessLevelOuter[0] = outer_tess_level.x;\n"
" gl_TessLevelOuter[1] = outer_tess_level.y;\n"
" gl_TessLevelOuter[2] = outer_tess_level.z;\n"
" gl_TessLevelOuter[3] = outer_tess_level.w;\n"
"}\n";
const char* tc_body_true = "${VERSION}\n"
"\n"
"${TESSELLATION_SHADER_REQUIRE}\n"
"\n"
"layout (vertices = MAX_VERTICES) out;\n"
"\n"
"uniform vec2 inner_tess_level;\n"
"uniform vec4 outer_tess_level;\n"
"\n"
"void main()\n"
"{\n"
" gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
"\n"
" gl_TessLevelInner[0] = inner_tess_level.x;\n"
" gl_TessLevelInner[1] = inner_tess_level.y;\n"
" gl_TessLevelOuter[0] = outer_tess_level.x;\n"
" gl_TessLevelOuter[1] = outer_tess_level.y;\n"
" gl_TessLevelOuter[2] = outer_tess_level.z;\n"
" gl_TessLevelOuter[3] = outer_tess_level.w;\n"
"}\n";
const char* n_patch_vertices_raw_ptr = NULL;
std::stringstream n_patch_vertices_sstream;
std::string n_patch_vertices_string;
std::string token = "MAX_VERTICES";
std::size_t token_index = std::string::npos;
n_patch_vertices_sstream << n_patch_vertices;
n_patch_vertices_string = n_patch_vertices_sstream.str();
n_patch_vertices_raw_ptr = n_patch_vertices_string.c_str();
result = (should_use_glInvocationID_indexed_input) ? tc_body_true : tc_body_false;
while ((token_index = result.find(token)) != std::string::npos)
{
result = result.replace(token_index, token.length(), n_patch_vertices_raw_ptr);
token_index = result.find(token);
}
return result;
}
/** Retrieves generic tessellation evaluation shader source code.
* The shader users user-specified tessellation properties.
*
* @param vertex_spacing Vertex spacing mode to use in the shader.
* @param primitive_mode Primitive mode to use in the shader.
* @param point_mode true to use point_mode in the shader, false
* to omit it.
*
* @return Requested string.
*/
std::string TessellationShaderUtils::getGenericTECode(_tessellation_shader_vertex_spacing vertex_spacing,
_tessellation_primitive_mode primitive_mode,
_tessellation_shader_vertex_ordering vertex_ordering,
bool point_mode)
{
std::string result;
const char* te_body = "${VERSION}\n"
"\n"
"${TESSELLATION_SHADER_REQUIRE}\n"
"\n"
"layout (TESSELLATOR_PRIMITIVE_MODE VERTEX_SPACING_MODE VERTEX_ORDERING POINT_MODE) in;\n"
"\n"
"out vec3 result_uvw;\n"
"\n"
"void main()\n"
"{\n"
" gl_Position = gl_in[0].gl_Position;\n"
" result_uvw = gl_TessCoord;\n"
"}\n";
const char* point_mode_token = "POINT_MODE";
std::size_t point_mode_token_index = std::string::npos;
std::string primitive_mode_string = TessellationShaderUtils::getESTokenForPrimitiveMode(primitive_mode);
const char* primitive_mode_token = "TESSELLATOR_PRIMITIVE_MODE";
std::size_t primitive_mode_token_index = std::string::npos;
std::string vertex_ordering_string;
const char* vertex_ordering_token = "VERTEX_ORDERING";
std::size_t vertex_ordering_token_index = std::string::npos;
std::string vertex_spacing_mode_string;
const char* vertex_spacing_token = "VERTEX_SPACING_MODE";
std::size_t vertex_spacing_token_index = std::string::npos;
result = te_body;
/* Prepare the vertex ordering token. We need to do this manually, because the default vertex spacing
* mode translates to empty string and the shader would fail to compile if we hadn't taken care of the
* comma
*/
if (vertex_ordering == TESSELLATION_SHADER_VERTEX_ORDERING_DEFAULT)
{
vertex_ordering_string = TessellationShaderUtils::getESTokenForVertexOrderingMode(vertex_ordering);
}
else
{
std::stringstream helper_sstream;
helper_sstream << ", " << TessellationShaderUtils::getESTokenForVertexOrderingMode(vertex_ordering);
vertex_ordering_string = helper_sstream.str();
}
/* Do the same for vertex spacing token */
if (vertex_spacing == TESSELLATION_SHADER_VERTEX_SPACING_DEFAULT)
{
vertex_spacing_mode_string = TessellationShaderUtils::getESTokenForVertexSpacingMode(vertex_spacing);
}
else
{
std::stringstream helper_sstream;
helper_sstream << ", " << TessellationShaderUtils::getESTokenForVertexSpacingMode(vertex_spacing);
vertex_spacing_mode_string = helper_sstream.str();
}
/* Primitive mode */
while ((primitive_mode_token_index = result.find(primitive_mode_token)) != std::string::npos)
{
result = result.replace(primitive_mode_token_index, strlen(primitive_mode_token), primitive_mode_string);
primitive_mode_token_index = result.find(primitive_mode_token);
}
/* Vertex ordering */
while ((vertex_ordering_token_index = result.find(vertex_ordering_token)) != std::string::npos)
{
result = result.replace(vertex_ordering_token_index, strlen(vertex_ordering_token), vertex_ordering_string);
vertex_ordering_token_index = result.find(vertex_ordering_token);
}
/* Vertex spacing */
while ((vertex_spacing_token_index = result.find(vertex_spacing_token)) != std::string::npos)
{
result = result.replace(vertex_spacing_token_index, strlen(vertex_spacing_token), vertex_spacing_mode_string);
vertex_spacing_token_index = result.find(vertex_spacing_token);
}
/* Point mode */
while ((point_mode_token_index = result.find(point_mode_token)) != std::string::npos)
{
result = result.replace(point_mode_token_index, strlen(point_mode_token), (point_mode) ? ", point_mode" : "");
point_mode_token_index = result.find(point_mode_token);
}
return result;
}
/** Initializes ES objects that will be needed for non-static calls
*
* This function throws TestError exception if an error occurs.
*
**/
void TessellationShaderUtils::init()
{
if (!m_parent_test->m_is_tessellation_shader_supported)
{
throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED);
}
/* Create buffer object used to hold XFB data */
m_gl.genBuffers(1, &m_bo_id);
GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGenBuffers() failed");
/* Create query object */
m_gl.genQueries(1, &m_qo_pg_id);
GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGenQueries() failed");
/* Initialize shader objects */
m_fs_id = m_gl.createShader(GL_FRAGMENT_SHADER);
m_vs_id = m_gl.createShader(GL_VERTEX_SHADER);
GLU_EXPECT_NO_ERROR(m_gl.getError(), "glCreateShader() failed");
/* Initialize bodies of the shaders and try to compile them */
const glw::GLuint shaders[] = { m_fs_id, m_vs_id };
const unsigned int n_shaders = DE_LENGTH_OF_ARRAY(shaders);
const char* fs_body = "${VERSION}\n"
"\n"
"void main()\n"
"{\n"
"}\n";
const char* vs_body = "${VERSION}\n"
"\n"
"void main()\n"
"{\n"
" gl_Position = vec4(1.0, 0.0, 0.0, 1.0);\n"
"}\n";
m_parent_test->shaderSourceSpecialized(m_fs_id, 1 /* count */, &fs_body);
m_parent_test->shaderSourceSpecialized(m_vs_id, 1 /* count */, &vs_body);
GLU_EXPECT_NO_ERROR(m_gl.getError(), "glShaderSource() failed");
compileShaders(n_shaders, shaders, true);
}
/** Retrieves amount of vertices that will be generated for a draw call
* that uses a program object, which is built of (at least) one tessellation
* stage.
*
* NOTE: This function can temporarily unbind active program object.
* This function throws TestError exception if an error occurs.
*
* @param primitive_mode Primitive mode used for the tessellation.
* @param inner_tessellation_level Two FP values that define inner tessellation levels.
* Must NOT be NULL.
* @param outer_tessellation_level Four FP values that define outer tessellation levels.
* Must NOT be NULL.
* @param vertex_spacing Vertex spacing mode used for the tessellation.
* @param is_point_mode_enabled true if point_mode should be enabled for the query,
* false otherwise.
*
* This function REQUIRES GL_EXT_geometry_shader support.
* This function throws TestError exception, should an error occur.
*
* @return Amount of vertices that would be generated by the tessellator unit for
* a particular draw call.
**/
unsigned int TessellationShaderUtils::getAmountOfVerticesGeneratedByTessellator(
_tessellation_primitive_mode primitive_mode, const float* inner_tessellation_level,
const float* outer_tessellation_level, _tessellation_shader_vertex_spacing vertex_spacing,
bool is_point_mode_enabled)
{
unsigned int result = 0;
switch (primitive_mode)
{
case TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES:
case TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS:
case TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES:
{
/* We refer to a counter program, TC and TE stages are configured as specified
* by the caller. Before we issue the draw call, we begin a query introduced in
* GL_EXT_geometry_shader that allows us to count how many primitives would have been generated. Doing so
* allows us to determine how many vertices were generated during the processed.
*/
const glw::GLint test_n_patch_vertices = 1;
_tessellation_vertex_counter_program test_program(m_gl);
initTessellationVertexCounterProgram(inner_tessellation_level, outer_tessellation_level, test_n_patch_vertices,
vertex_spacing, primitive_mode, is_point_mode_enabled, test_program);
result = test_program.n_data_vertices;
break;
}
default:
{
TCU_FAIL("Unrecognized primitive mode");
}
} /* switch (primitive_mode) */
return result;
}
/** Retrieves data generated by a tessellator for a particular tessellation configuration.
*
* @param inner Two FP values defining inner tessellation values to be used for tessellation.
* Must not be NULL.
* @param point_mode true if point mode is to be used for tessellation, false otherwise.
* @param primitive_mode Primitive mode to be used for tessellation.
* @param vertex_ordering Vertex ordering to be used for tessellation.
* @param vertex_spacing Vertex spacing to be used for tessellation.
* @param outer Four FP values defining outer tessellation values to be used for tessellation.
* Must not be NULL.
*
* @return Pointer to buffer containing tessellated coordinates.
**/
std::vector<char> TessellationShaderUtils::getDataGeneratedByTessellator(
const float* inner, bool point_mode, _tessellation_primitive_mode primitive_mode,
_tessellation_shader_vertex_ordering vertex_ordering, _tessellation_shader_vertex_spacing vertex_spacing,
const float* outer)
{
(void)vertex_ordering;
glw::GLint test_n_patch_vertices = getPatchVerticesForPrimitiveMode(primitive_mode);
_tessellation_vertex_counter_program test_program(m_gl);
initTessellationVertexCounterProgram(inner, outer, test_n_patch_vertices, vertex_spacing, primitive_mode,
point_mode, test_program);
return test_program.m_data;
}
/** Retrieves ESSL token corresponding to particular primitive mode.
* Will throw TestError exception if @param primitive_mode is not
* valid.
*
* @param primitive_mode Primitive mode to consider.
*
* @return String telling how the primitive mode would be expressed in
* ES SL.
**/
std::string TessellationShaderUtils::getESTokenForPrimitiveMode(_tessellation_primitive_mode primitive_mode)
{
std::string result = "?";
switch (primitive_mode)
{
case TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES:
{
result = "isolines";
break;
}
case TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS:
{
result = "quads";
break;
}
case TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES:
{
result = "triangles";
break;
}
default:
{
TCU_FAIL("Unrecognized tessellation primitive mode");
}
}
return result;
}
/** Retrieves ESSL token corresponding to particular vertex ordering.
* Will throw TestError exception if @param vertex_ordering is not
* valid.
*
* @param vertex_ordering Vertex ordering to consider.
*
* @return String telling how the vertex mode would be expressed in
* ES SL.
**/
std::string TessellationShaderUtils::getESTokenForVertexOrderingMode(
_tessellation_shader_vertex_ordering vertex_ordering)
{
std::string result;
switch (vertex_ordering)
{
case TESSELLATION_SHADER_VERTEX_ORDERING_CCW:
{
result = "ccw";
break;
}
case TESSELLATION_SHADER_VERTEX_ORDERING_CW:
{
result = "cw";
break;
}
case TESSELLATION_SHADER_VERTEX_ORDERING_DEFAULT:
{
/* Simply return an empty token */
result = "";
break;
}
default:
{
TCU_FAIL("Unrecognized tessellation shader vertex ordering");
}
} /* switch (vertex_ordering) */
return result;
}
/** Retrieves ESSL token corresponding to particular vertex spacing mode.
* Will throw TestError exception if @param vertex_spacing is not
* valid.
*
* @param vertex_spacing Vertex spacing mode to consider.
*
* @return String telling how the vertex spacing mode would be expressed in
* ES SL.
**/
std::string TessellationShaderUtils::getESTokenForVertexSpacingMode(_tessellation_shader_vertex_spacing vertex_spacing)
{
std::string result;
switch (vertex_spacing)
{
case TESSELLATION_SHADER_VERTEX_SPACING_DEFAULT:
{
result = "";
break;
}
case TESSELLATION_SHADER_VERTEX_SPACING_EQUAL:
{
result = "equal_spacing";
break;
}
case TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_EVEN:
{
result = "fractional_even_spacing";
break;
}
case TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD:
{
result = "fractional_odd_spacing";
break;
}
default:
{
TCU_FAIL("Invalid vertex spacing mode requested");
}
}
return result;
}
/** Tells how many vertices should be passed in a single patch for
* particular primitive mode to work for tessellation stage.
*
* Throws TestError exception if @param primitive_mode is invalid.
*
* @param primitive_mode Primitive mode to consider.
*
* @return Requested value.
**/
glw::GLint TessellationShaderUtils::getPatchVerticesForPrimitiveMode(_tessellation_primitive_mode primitive_mode)
{
glw::GLint result = 0;
switch (primitive_mode)
{
case TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES:
result = 4;
break;
case TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS:
result = 4;
break;
case TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES:
result = 3;
break;
default:
{
TCU_FAIL("Unrecognized primitive mode");
}
} /* switch (primitive_mode) */
return result;
}
/** Retrieves tessellation level used by the tessellator, given
* vertex spacing setting.
*
* @param vertex_spacing Vertex spacing used for tessellation
* evaluation stage;
* @param level Tessellation level as defined in TC
* stage OR as configured with
* GL_PATCH_DEFAULT_*_LEVEL pnames.
* @param gl_max_tess_gen_level_value GL_MAX_TESS_GEN_LEVEL_EXT pname value,
* as reported by the implementation.
* @param out_clamped Deref will be used to store clamped (but
* not rounded) representation. Can be NULL.
* @param out_clamped_and_rounded Deref will be used to store clamped and
* rounded representation. Can be NULL.
**/
void TessellationShaderUtils::getTessellationLevelAfterVertexSpacing(_tessellation_shader_vertex_spacing vertex_spacing,
float level,
glw::GLint gl_max_tess_gen_level_value,
float* out_clamped, float* out_clamped_and_rounded)
{
/* Behavior is as per EXT_tessellation_shader spec */
switch (vertex_spacing)
{
case TESSELLATION_SHADER_VERTEX_SPACING_DEFAULT:
case TESSELLATION_SHADER_VERTEX_SPACING_EQUAL:
{
if (level < 1.0f)
{
level = 1.0f;
}
else if (level > (float)gl_max_tess_gen_level_value)
{
level = (float)gl_max_tess_gen_level_value;
}
if (out_clamped != DE_NULL)
{
*out_clamped = level;
}
/* Round *up* to nearest integer */
level = (float)((int)(deFloatCeil(level) + 0.5f));
if (out_clamped_and_rounded != DE_NULL)
{
*out_clamped_and_rounded = level;
}
break;
}
case TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_EVEN:
{
if (level < 2.0f)
{
level = 2.0f;
}
else if (level > (float)gl_max_tess_gen_level_value)
{
level = (float)gl_max_tess_gen_level_value;
}
if (out_clamped != DE_NULL)
{
*out_clamped = level;
}
/* Round *up* to nearest *even* integer */
int level_temp = (int)(deFloatCeil(level) + 0.5f);
if ((level_temp % 2) != 0)
{
level_temp++;
}
if (out_clamped_and_rounded != DE_NULL)
{
*out_clamped_and_rounded = (float)level_temp;
}
break;
}
case TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD:
{
if (level < 1.0f)
{
level = 1.0f;
}
else if (level > (float)(gl_max_tess_gen_level_value - 1))
{
level = (float)(gl_max_tess_gen_level_value - 1);
}
if (out_clamped != DE_NULL)
{
*out_clamped = level;
}
/* Round to *up* nearest *odd* integer */
int level_temp = (int)(deFloatCeil(level) + 0.5f);
if ((level_temp % 2) != 1)
{
level_temp++;
}
if (out_clamped_and_rounded != DE_NULL)
{
*out_clamped_and_rounded = (float)level_temp;
}
break;
}
default:
{
TCU_FAIL("Unrecognized vertex spacing mode");
}
} /* switch(vertex_spacing) */
}
/** Returns a vector of _tessellation_levels instances with different level values.
*
* @param primitive_mode Primitive mode to consider.
* @param gl_max_tess_gen_level_value Implementation-specific GL_MAX_TESS_GEN_LEVEL_EXT value.
* @param filter Condition which all generated tuples should meet in
* order to land in the result vector.
*
* @return _tessellation_levels_set instance storing described values.
**/
_tessellation_levels_set TessellationShaderUtils::getTessellationLevelSetForPrimitiveMode(
_tessellation_primitive_mode primitive_mode, glw::GLint gl_max_tess_gen_level_value,
_tessellation_level_set_filter filter)
{
/* As a starter value, use a tessellation level that is different for each
* primitive modes, just to make sure the implementation can correctly
* handle various tessellation level values */
glw::GLint n_min_patch_vertices = getPatchVerticesForPrimitiveMode(primitive_mode);
glw::GLint n_half_max_patch_vertices_mul_min = gl_max_tess_gen_level_value / 2;
glw::GLint n_max_patch_vertices_mul_min = gl_max_tess_gen_level_value;
if ((n_half_max_patch_vertices_mul_min % n_min_patch_vertices) != 0)
{
/* Round to nearest mul-of-min integer */
n_half_max_patch_vertices_mul_min +=
(n_min_patch_vertices - (gl_max_tess_gen_level_value / 2) % n_min_patch_vertices);
}
if ((n_max_patch_vertices_mul_min % n_min_patch_vertices) != 0)
{
/* Round to previous nearest mul-of-min integer */
n_max_patch_vertices_mul_min -= (gl_max_tess_gen_level_value % n_min_patch_vertices);
}
/* Prepare the result vector items */
_tessellation_levels_set result;
if ((filter & TESSELLATION_LEVEL_SET_FILTER_ALL_LEVELS_USE_THE_SAME_VALUE) != 0)
{
/* Prepare the result vector items */
_tessellation_levels item_1;
_tessellation_levels item_2;
_tessellation_levels item_3;
item_1.inner[0] = float(n_min_patch_vertices);
item_1.inner[1] = float(n_min_patch_vertices + 1);
item_1.outer[0] = float(n_min_patch_vertices + 3);
item_1.outer[1] = float(n_min_patch_vertices + 2);
item_1.outer[2] = float(n_min_patch_vertices + 1);
item_1.outer[3] = float(n_min_patch_vertices);
item_2.inner[0] = float(n_half_max_patch_vertices_mul_min);
item_2.inner[1] = float(n_half_max_patch_vertices_mul_min - 1);
item_2.outer[0] = float(n_half_max_patch_vertices_mul_min - 3);
item_2.outer[1] = float(n_half_max_patch_vertices_mul_min - 2);
item_2.outer[2] = float(n_half_max_patch_vertices_mul_min - 1);
item_2.outer[3] = float(n_half_max_patch_vertices_mul_min);
item_3.inner[0] = float(n_max_patch_vertices_mul_min - 1);
item_3.inner[1] = float(n_max_patch_vertices_mul_min - 2);
item_3.outer[0] = float(n_max_patch_vertices_mul_min - 3);
item_3.outer[1] = float(n_max_patch_vertices_mul_min - 4);
item_3.outer[2] = float(n_max_patch_vertices_mul_min - 5);
item_3.outer[3] = float(n_max_patch_vertices_mul_min - 6);
/* Push the items onto result vector. */
result.push_back(item_1);
result.push_back(item_2);
result.push_back(item_3);
}
else
{
DE_ASSERT((filter & TESSELLATION_LEVEL_SET_FILTER_ALL_COMBINATIONS) != 0 ||
(filter & TESSELLATION_LEVEL_SET_FILTER_INNER_AND_OUTER_LEVELS_USE_DIFFERENT_VALUES) != 0);
const glw::GLint base_values[] = { -1, 1, n_half_max_patch_vertices_mul_min, n_max_patch_vertices_mul_min };
const unsigned int n_base_values = DE_LENGTH_OF_ARRAY(base_values);
const unsigned int n_relevant_inner_tess_levels =
(primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES) ?
0 :
(primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS) ? 2 : 1;
const unsigned int n_relevant_outer_tess_levels =
(primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES) ?
2 :
(primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS) ? 4 : 3;
for (unsigned int n_inner0_base_value = 0;
n_inner0_base_value < ((n_relevant_inner_tess_levels > 0) ? n_base_values : 1); ++n_inner0_base_value)
{
const glw::GLint inner0_value = base_values[n_inner0_base_value];
for (unsigned int n_inner1_base_value = 0;
n_inner1_base_value < ((n_relevant_inner_tess_levels > 1) ? n_base_values : 1); ++n_inner1_base_value)
{
const glw::GLint inner1_value = base_values[n_inner1_base_value];
for (unsigned int n_outer0_base_value = 0;
n_outer0_base_value < ((n_relevant_outer_tess_levels > 0) ? n_base_values : 1);
++n_outer0_base_value)
{
const glw::GLint outer0_value = base_values[n_outer0_base_value];
for (unsigned int n_outer1_base_value = 0;
n_outer1_base_value < ((n_relevant_outer_tess_levels > 1) ? n_base_values : 1);
++n_outer1_base_value)
{
const glw::GLint outer1_value = base_values[n_outer1_base_value];
for (unsigned int n_outer2_base_value = 0;
n_outer2_base_value < ((n_relevant_outer_tess_levels > 2) ? n_base_values : 1);
++n_outer2_base_value)
{
const glw::GLint outer2_value = base_values[n_outer2_base_value];
for (unsigned int n_outer3_base_value = 0;
n_outer3_base_value < ((n_relevant_outer_tess_levels > 3) ? n_base_values : 1);
++n_outer3_base_value)
{
const glw::GLint outer3_value = base_values[n_outer3_base_value];
/* Skip combinations where any of the relevant outer tessellation level values
* is negative. These would cause no tessellation coordinates to be generated
* by the tessellator.
*/
if ((n_relevant_outer_tess_levels > 0 && outer0_value < 0) ||
(n_relevant_outer_tess_levels > 1 && outer1_value < 0) ||
(n_relevant_outer_tess_levels > 2 && outer2_value < 0) ||
(n_relevant_outer_tess_levels > 3 && outer3_value < 0))
{
continue;
}
/* If TESSELLATION_LEVEL_SET_FILTER_INNER_AND_OUTER_LEVELS_USE_DIFFERENT_VALUES
* filter was requested, make sure the values used separately for inner and outer
* tess levels are actually different. */
if ((filter &
TESSELLATION_LEVEL_SET_FILTER_INNER_AND_OUTER_LEVELS_USE_DIFFERENT_VALUES) != 0)
{
DE_ASSERT(n_base_values >= 4 /* outer tess levels supported */);
if ((n_relevant_inner_tess_levels > 1 && inner0_value == inner1_value) ||
(n_relevant_outer_tess_levels > 1 && outer0_value == outer1_value) ||
(n_relevant_outer_tess_levels > 2 && outer1_value == outer2_value) ||
(n_relevant_outer_tess_levels > 3 && outer2_value == outer3_value))
{
continue;
}
} /* if ((filter & TESSELLATION_LEVEL_SET_FILTER_INNER_AND_OUTER_LEVELS_USE_DIFFERENT_VALUES) != 0) */
/* If TESSELLATION_LEVEL_SET_FILTER_EXCLUDE_NEGATIVE_BASE_VALUE, make sure
* no inner/outer tessellation level we're about to use is negative. */
if ((filter & TESSELLATION_LEVEL_SET_FILTER_EXCLUDE_NEGATIVE_BASE_VALUE) != 0)
{
if ((n_relevant_inner_tess_levels > 0 && inner0_value < 0) ||
(n_relevant_inner_tess_levels > 1 && inner1_value < 0) ||
(n_relevant_outer_tess_levels > 0 && outer0_value < 0) ||
(n_relevant_outer_tess_levels > 1 && outer1_value < 0) ||
(n_relevant_outer_tess_levels > 2 && outer2_value < 0) ||
(n_relevant_outer_tess_levels > 3 && outer3_value < 0))
{
continue;
}
}
/* Construct the tess level combination */
_tessellation_levels item;
item.inner[0] = (glw::GLfloat)inner0_value;
item.inner[1] = (glw::GLfloat)inner1_value;
item.outer[0] = (glw::GLfloat)outer0_value;
item.outer[1] = (glw::GLfloat)outer1_value;
item.outer[2] = (glw::GLfloat)outer2_value;
item.outer[3] = (glw::GLfloat)outer3_value;
/* Store it */
result.push_back(item);
} /* for (all outer[3] base values) */
} /* for (all outer[2] base values) */
} /* for (all outer[1] base values) */
} /* for (all outer[0] base values) */
} /* for (all inner[1] base values) */
} /* for (all inner[0] base values) */
}
return result;
}
/** Retrieves transform feedback mode that should be used for glBeginTransformFeedback()
* call, if TF is to be active while a tessellated draw call is made.
*
* This function throws TestError exception if @param primitive_mode is invalid.
*
* @param primitive_mode Primitive mode to consider
* @param is_point_mode true if tessellation is run in point_mode mode, false otherwise.
*
* @return Corresponding ES enum.
**/
glw::GLenum TessellationShaderUtils::getTFModeForPrimitiveMode(_tessellation_primitive_mode primitive_mode,
bool is_point_mode)
{
glw::GLenum result = GL_NONE;
if (is_point_mode)
{
result = GL_POINTS;
}
else
{
switch (primitive_mode)
{
case TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES:
{
result = GL_LINES;
break;
}
case TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS:
{
result = GL_TRIANGLES;
break;
}
case TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES:
{
result = GL_TRIANGLES;
break;
}
default:
{
TCU_FAIL("Unrecognized primitive mode");
}
} /* switch (primitive_mode) */
}
return result;
}
/** Initializes a counter program.
*
* This function throws a TestError exception, should an error occur.
*
* @param inner_tess_level Two FP values to be used for inner tessellation levels. Must not be NULL.
* @param outer_tess_level Four FP values to be used for outer tessellation levels. Must not be NULL.
* @param n_patch_vertices Amount of TC stage output patch vertices.
* @param vertex_spacing Vertex spacing mode to be used for tessellation.
* @param primitive_mode Primitive mode to be used for tessellation.
* @param is_point_mode_enabled true if the point mode should be enabled for the program, false otherwise.
* @param result_descriptor Objects created during initialization will be stored in the referenced descriptor.
*
**/
void TessellationShaderUtils::initTessellationVertexCounterProgram(
const float* inner_tess_level, const float* outer_tess_level, glw::GLint n_patch_vertices,
_tessellation_shader_vertex_spacing vertex_spacing, _tessellation_primitive_mode primitive_mode,
bool is_point_mode_enabled, _tessellation_vertex_counter_program& result_descriptor)
{
glw::GLint po_id = 0;
glw::GLint tc_id = 0;
glw::GLint te_id = 0;
/* Generate the shader objects */
tc_id = m_gl.createShader(m_parent_test->m_glExtTokens.TESS_CONTROL_SHADER);
te_id = m_gl.createShader(m_parent_test->m_glExtTokens.TESS_EVALUATION_SHADER);
GLU_EXPECT_NO_ERROR(m_gl.getError(), "Could not create TC/TE shader objects");
/* Generate the program object */
po_id = m_gl.createProgram();
GLU_EXPECT_NO_ERROR(m_gl.getError(), "Could not create a program object");
/* Initialize the shaders.
*
* Note: it's fine to use CCW ordering here, since it does not affect the amount
* of primitives generated by the tessellator.
**/
std::string tc_code = getGenericTCCode(n_patch_vertices, false);
const char* tc_code_ptr = tc_code.c_str();
std::string te_code = getGenericTECode(vertex_spacing, primitive_mode, TESSELLATION_SHADER_VERTEX_ORDERING_CCW,
is_point_mode_enabled);
const char* te_code_ptr = te_code.c_str();
/* Set up XFB */
const char* varyings[] = { "result_uvw" };
m_gl.transformFeedbackVaryings(po_id, 1, /* count */
varyings, GL_INTERLEAVED_ATTRIBS);
GLU_EXPECT_NO_ERROR(m_gl.getError(), "glTransformFeedbackVaryings() call failed");
/* Link the program and check that the linking has succeeded */
bool build_success = m_parent_test->buildProgram(po_id, m_fs_id, 0 /* precompiled */, NULL, tc_id, 1, &tc_code_ptr,
te_id, 1, &te_code_ptr, m_vs_id, 0 /* precompiled */, NULL);
if (!build_success)
{
TCU_FAIL("Compilation and/or linking failed");
}
/* Set up the inner/outer tess level uniforms */
glw::GLint inner_tess_level_uniform_location = -1;
glw::GLint outer_tess_level_uniform_location = -1;
inner_tess_level_uniform_location = m_gl.getUniformLocation(po_id, "inner_tess_level");
outer_tess_level_uniform_location = m_gl.getUniformLocation(po_id, "outer_tess_level");
DE_ASSERT(inner_tess_level_uniform_location != -1);
DE_ASSERT(outer_tess_level_uniform_location != -1);
m_gl.useProgram(po_id);
GLU_EXPECT_NO_ERROR(m_gl.getError(), "glUseProgram() call failed");
m_gl.uniform2fv(inner_tess_level_uniform_location, 1, /* count */
inner_tess_level);
GLU_EXPECT_NO_ERROR(m_gl.getError(), "glUniform2fv() call failed");
m_gl.uniform4fv(outer_tess_level_uniform_location, 1, /* count */
outer_tess_level);
GLU_EXPECT_NO_ERROR(m_gl.getError(), "glUniform4fv() call failed");
/* Initialize the test descriptor */
memcpy(result_descriptor.inner_tess_level, inner_tess_level, sizeof(result_descriptor.inner_tess_level));
memcpy(result_descriptor.outer_tess_level, outer_tess_level, sizeof(result_descriptor.outer_tess_level));
result_descriptor.is_point_mode_enabled = is_point_mode_enabled;
result_descriptor.n_patch_vertices = n_patch_vertices;
result_descriptor.po_id = po_id;
result_descriptor.primitive_mode = primitive_mode;
result_descriptor.tc_id = tc_id;
result_descriptor.te_id = te_id;
result_descriptor.tess_level_inner_uniform_location = inner_tess_level_uniform_location;
result_descriptor.tess_level_outer_uniform_location = outer_tess_level_uniform_location;
result_descriptor.vertex_spacing = vertex_spacing;
captureTessellationData(result_descriptor);
}
/** Tells whether user-provided vertex (expressed in tessellation space) generated
* during triangle tessellation is an outer edge vertex.
*
* @param primitive_mode Primitive mode, for which the tessellated vertex
* data was generated.
* @param tessellated_vertex_data Vertex data to check. Must define 3 floats.
*
* @return true if the vertex is a part of an outer edge, false otherwise.
**/
bool TessellationShaderUtils::isOuterEdgeVertex(_tessellation_primitive_mode primitive_mode,
const float* tessellated_vertex_data)
{
const float epsilon = 1e-5f;
bool result = false;
switch (primitive_mode)
{
case TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES:
{
if (de::abs(tessellated_vertex_data[0]) < epsilon || de::abs(tessellated_vertex_data[0] - 1.0f) < epsilon ||
de::abs(tessellated_vertex_data[1]) < epsilon || de::abs(tessellated_vertex_data[1] - 1.0f) < epsilon ||
de::abs(tessellated_vertex_data[2]) < epsilon || de::abs(tessellated_vertex_data[2] - 1.0f) < epsilon)
{
/* Make sure vertex is inside the triangle */
if (0.0f <= tessellated_vertex_data[0] && tessellated_vertex_data[0] <= 1.0f &&
0.0f <= tessellated_vertex_data[1] && tessellated_vertex_data[1] <= 1.0f &&
0.0f <= tessellated_vertex_data[2] && tessellated_vertex_data[2] <= 1.0f)
{
result = true;
}
}
break;
} /* case TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES: */
case TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS:
{
if (de::abs(tessellated_vertex_data[0]) < epsilon || de::abs(tessellated_vertex_data[0] - 1.0f) < epsilon ||
de::abs(tessellated_vertex_data[1]) < epsilon || de::abs(tessellated_vertex_data[1] - 1.0f) < epsilon)
{
result = true;
}
break;
} /* case TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS: */
default:
{
DE_FATAL("Unrecognized primitive mode");
}
}
return result;
}
/** Returns true if two triangles are the same. Takes potentially different
* vertex ordering into consideration.
*
* @param triangle_vertex_data Reference 9 FP values defining a triangle
* that the triangle defined by @param vertex_data
* will be compared against.
* @param vertex_data 9 FP values defining a triangle, perhaps in
* a different order, that @param triangle_vertex_data
* will be checked against.
*
* @return true if the triangles are the same; false otherwise.
*
**/
bool TessellationShaderUtils::isTriangleDefined(const float* triangle_vertex_data, const float* vertex_data)
{
const float epsilon = 1e-5f;
bool has_triangle_vertex1_been_found = false;
bool has_triangle_vertex2_been_found = false;
bool has_triangle_vertex3_been_found = false;
bool result = false;
if ((de::abs(triangle_vertex_data[0] - vertex_data[0]) < epsilon &&
de::abs(triangle_vertex_data[1] - vertex_data[1]) < epsilon &&
de::abs(triangle_vertex_data[2] - vertex_data[2]) < epsilon) ||
(de::abs(triangle_vertex_data[3] - vertex_data[0]) < epsilon &&
de::abs(triangle_vertex_data[4] - vertex_data[1]) < epsilon &&
de::abs(triangle_vertex_data[5] - vertex_data[2]) < epsilon) ||
(de::abs(triangle_vertex_data[6] - vertex_data[0]) < epsilon &&
de::abs(triangle_vertex_data[7] - vertex_data[1]) < epsilon &&
de::abs(triangle_vertex_data[8] - vertex_data[2]) < epsilon))
{
has_triangle_vertex1_been_found = true;
}
if ((de::abs(triangle_vertex_data[0] - vertex_data[3]) < epsilon &&
de::abs(triangle_vertex_data[1] - vertex_data[4]) < epsilon &&
de::abs(triangle_vertex_data[2] - vertex_data[5]) < epsilon) ||
(de::abs(triangle_vertex_data[3] - vertex_data[3]) < epsilon &&
de::abs(triangle_vertex_data[4] - vertex_data[4]) < epsilon &&
de::abs(triangle_vertex_data[5] - vertex_data[5]) < epsilon) ||
(de::abs(triangle_vertex_data[6] - vertex_data[3]) < epsilon &&
de::abs(triangle_vertex_data[7] - vertex_data[4]) < epsilon &&
de::abs(triangle_vertex_data[8] - vertex_data[5]) < epsilon))
{
has_triangle_vertex2_been_found = true;
}
if ((de::abs(triangle_vertex_data[0] - vertex_data[6]) < epsilon &&
de::abs(triangle_vertex_data[1] - vertex_data[7]) < epsilon &&
de::abs(triangle_vertex_data[2] - vertex_data[8]) < epsilon) ||
(de::abs(triangle_vertex_data[3] - vertex_data[6]) < epsilon &&
de::abs(triangle_vertex_data[4] - vertex_data[7]) < epsilon &&
de::abs(triangle_vertex_data[5] - vertex_data[8]) < epsilon) ||
(de::abs(triangle_vertex_data[6] - vertex_data[6]) < epsilon &&
de::abs(triangle_vertex_data[7] - vertex_data[7]) < epsilon &&
de::abs(triangle_vertex_data[8] - vertex_data[8]) < epsilon))
{
has_triangle_vertex3_been_found = true;
}
if (has_triangle_vertex1_been_found && has_triangle_vertex2_been_found && has_triangle_vertex3_been_found)
{
result = true;
}
return result;
}
} // namespace glcts