/*------------------------------------------------------------------------- * 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 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, ¤t_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, ¤t_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(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 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