/*------------------------------------------------------------------------- * 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 "esextcTessellationShaderTriangles.hpp" #include "gluContextInfo.hpp" #include "gluDefs.hpp" #include "glwEnums.hpp" #include "glwFunctions.hpp" #include "tcuTestLog.hpp" namespace glcts { /** Constructor * * @param context Test context **/ TessellationShaderTrianglesTests::TessellationShaderTrianglesTests(glcts::Context& context, const ExtParameters& extParams) : TestCaseGroupBase(context, extParams, "tessellation_shader_triangles_tessellation", "Verifies triangle tessellation functionality") { /* No implementation needed */ } /** * Initializes test groups for geometry shader tests **/ void TessellationShaderTrianglesTests::init(void) { addChild(new glcts::TessellationShaderTrianglesDegenerateTriangle(m_context, m_extParams)); addChild(new glcts::TessellationShaderTrianglesIdenticalTriangles(m_context, m_extParams)); addChild(new glcts::TessellationShaderTrianglesInnerTessellationLevelRounding(m_context, m_extParams)); } /** Constructor * * @param context Test context **/ TessellationShaderTrianglesDegenerateTriangle::TessellationShaderTrianglesDegenerateTriangle( Context& context, const ExtParameters& extParams) : TestCaseBase(context, extParams, "degenerate_triangle", "Verifies a degenerate triangle is generated by tessellator " "under a specific configuration of inner/outer tessellation " "levels & vertex spacing modes.") , m_bo_id(0) , m_fs_id(0) , m_tc_id(0) , m_vs_id(0) , m_vao_id(0) { /* Left blank on purpose */ } /** Deinitializes ES objects created for the test. */ void TessellationShaderTrianglesDegenerateTriangle::deinit() { /* Call base class' deinit() */ TestCaseBase::deinit(); if (!m_is_tessellation_shader_supported) { return; } const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Deinitialize TF buffer object bindings */ gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* buffer */); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* index */, 0 /* buffer */); /* Reset GL_PATCH_VERTICES_EXT value */ gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, 3); /* Unbind vertex array object */ gl.bindVertexArray(0); /* Free all ES objects we allocated for the test */ if (m_bo_id != 0) { gl.deleteBuffers(1, &m_bo_id); m_bo_id = 0; } if (m_fs_id != 0) { gl.deleteShader(m_fs_id); m_fs_id = 0; } if (m_tc_id != 0) { gl.deleteShader(m_tc_id); m_tc_id = 0; } if (m_vs_id != 0) { gl.deleteShader(m_vs_id); m_vs_id = 0; } if (m_vao_id != 0) { gl.deleteVertexArrays(1, &m_vao_id); m_vao_id = 0; } /* Deinitialize all test descriptors */ for (_tests::iterator it = m_tests.begin(); it != m_tests.end(); ++it) { deinitTestDescriptor(*it); } m_tests.clear(); } /** Deinitialize all test pass-specific ES objects. * * @param test Descriptor of a test pass to deinitialize. **/ void TessellationShaderTrianglesDegenerateTriangle::deinitTestDescriptor(_test_descriptor& test) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); if (test.po_id != 0) { gl.deleteProgram(test.po_id); test.po_id = 0; } if (test.te_id != 0) { gl.deleteShader(test.te_id); test.te_id = 0; } } /** Initializes ES objects necessary to run the test. */ void TessellationShaderTrianglesDegenerateTriangle::initTest() { /* Skip if required extensions are not supported. */ if (!m_is_tessellation_shader_supported) { throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED); } /* Generate all test-wide objects needed for test execution */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 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!"); gl.genBuffers(1, &m_bo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers() failed"); m_fs_id = gl.createShader(GL_FRAGMENT_SHADER); m_tc_id = gl.createShader(m_glExtTokens.TESS_CONTROL_SHADER); m_vs_id = gl.createShader(GL_VERTEX_SHADER); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() failed"); /* Configure fragment shader body */ const char* fs_body = "${VERSION}\n" "\n" "void main()\n" "{\n" "}\n"; shaderSourceSpecialized(m_fs_id, 1 /* count */, &fs_body); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for fragment shader"); /* Configure tessellation control shader body */ const char* tc_body = "${VERSION}\n" "\n" "${TESSELLATION_SHADER_REQUIRE}\n" "\n" "layout (vertices=3) out;\n" "\n" "void main()\n" "{\n" " gl_out [gl_InvocationID].gl_Position = gl_in[0].gl_Position;\n" "\n" " gl_TessLevelInner[0] = 1.0;\n" " gl_TessLevelOuter[0] = 1.0;\n" " gl_TessLevelOuter[1] = 1.0;\n" " gl_TessLevelOuter[2] = 1.0;\n" "}\n"; shaderSourceSpecialized(m_tc_id, 1 /* count */, &tc_body); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for tessellation control shader"); /* Configure vertex shader body */ const char* vs_body = "${VERSION}\n" "\n" "void main()\n" "{\n" " gl_Position = vec4(1.0, 0.0, 0.0, 1.0);\n" "}\n"; shaderSourceSpecialized(m_vs_id, 1 /* count */, &vs_body); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for vertex shader"); /* Compile all the shaders */ const glw::GLuint shaders[] = { m_fs_id, m_tc_id, m_vs_id }; const unsigned int n_shaders = sizeof(shaders) / sizeof(shaders[0]); for (unsigned int n_shader = 0; n_shader < n_shaders; ++n_shader) { glw::GLuint shader = shaders[n_shader]; if (shader != 0) { glw::GLint compile_status = GL_FALSE; gl.compileShader(shader); GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader() failed"); gl.getShaderiv(shader, GL_COMPILE_STATUS, &compile_status); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv() failed"); if (compile_status != GL_TRUE) { TCU_FAIL("Shader compilation failed"); } } } /* for (all shaders) */ /* Initialize all test passes */ _test_descriptor test_equal_spacing; _test_descriptor test_fractional_odd_spacing; initTestDescriptor(test_equal_spacing, TESSELLATION_SHADER_VERTEX_SPACING_EQUAL); initTestDescriptor(test_fractional_odd_spacing, TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD); m_tests.push_back(test_equal_spacing); m_tests.push_back(test_fractional_odd_spacing); /* Set up buffer object storage */ gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_bo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() failed"); gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, sizeof(float) * 3 /* components */ * 3 /* UVW sets */, NULL, /* data */ GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() failed"); /* Bind the buffer object to indiced TF binding point */ gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* index */, m_bo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase() failed"); } /** Initializes all ES objects necessary to run a specific test pass. * * @param test Test descriptor to fill with IDs of initialized objects. * @param vertex_spacing Vertex spacing mode to use for the run. **/ void TessellationShaderTrianglesDegenerateTriangle::initTestDescriptor( _test_descriptor& test, _tessellation_shader_vertex_spacing vertex_spacing) { test.vertex_spacing = vertex_spacing; /* Set up a program object for the descriptor */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); test.po_id = gl.createProgram(); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() failed"); /* Set up a pass-specific tessellation evaluation shader object. */ test.te_id = gl.createShader(m_glExtTokens.TESS_EVALUATION_SHADER); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() failed"); /* Configure tessellation evaluation shader body */ const char* te_template = "${VERSION}\n" "\n" "${TESSELLATION_SHADER_REQUIRE}\n" "\n" "layout (triangles, VERTEX_SPACING_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* te_body_raw_ptr = DE_NULL; std::string te_body_string = te_template; std::string vertex_spacing_mode_string; const char* vertex_spacing_token = "VERTEX_SPACING_MODE"; std::size_t vertex_spacing_token_index = std::string::npos; switch (vertex_spacing) { case TESSELLATION_SHADER_VERTEX_SPACING_EQUAL: { vertex_spacing_mode_string = "equal_spacing"; break; } case TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD: { vertex_spacing_mode_string = "fractional_odd_spacing"; break; } default: { TCU_FAIL("Invalid vertex spacing mode requested"); } } while ((vertex_spacing_token_index = te_body_string.find(vertex_spacing_token)) != std::string::npos) { te_body_string = te_body_string.replace(vertex_spacing_token_index, strlen(vertex_spacing_token), vertex_spacing_mode_string); vertex_spacing_token_index = te_body_string.find(vertex_spacing_token); } te_body_raw_ptr = te_body_string.c_str(); shaderSourceSpecialized(test.te_id, 1 /* count */, &te_body_raw_ptr); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for tessellation evaluation shader"); /* Compile the tessellation evaluation shader */ glw::GLint compile_status = GL_FALSE; gl.compileShader(test.te_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader() failed for tessellation evaluation shader"); gl.getShaderiv(test.te_id, GL_COMPILE_STATUS, &compile_status); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv() failed for tessellation evaluation shader"); if (compile_status != GL_TRUE) { TCU_FAIL("Tessellation evaluation shader compilation failed"); } /* Attach all shader to the program object */ gl.attachShader(test.po_id, m_fs_id); gl.attachShader(test.po_id, m_tc_id); gl.attachShader(test.po_id, test.te_id); gl.attachShader(test.po_id, m_vs_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader() failed"); /* Set up XFB */ const char* varyings[] = { "result_uvw" }; const unsigned int n_varyings = sizeof(varyings) / sizeof(varyings[0]); gl.transformFeedbackVaryings(test.po_id, n_varyings, varyings, GL_INTERLEAVED_ATTRIBS); GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings() failed"); /* Link the program object */ glw::GLint link_status = GL_FALSE; gl.linkProgram(test.po_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glLinkProgram() failed"); gl.getProgramiv(test.po_id, GL_LINK_STATUS, &link_status); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv() failed"); if (link_status != GL_TRUE) { TCU_FAIL("Program linking failed"); } } /** 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 TessellationShaderTrianglesDegenerateTriangle::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 ES test objects */ initTest(); /* We only need to use one vertex per so go for it */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, 1); GLU_EXPECT_NO_ERROR(gl.getError(), "glPatchParameteriEXT() failed for GL_PATCH_VERTICES_EXT pname"); /* Iterate through all tests configured */ for (_tests_const_iterator test_iterator = m_tests.begin(); test_iterator != m_tests.end(); test_iterator++) { const _test_descriptor& test = *test_iterator; /* Run the iteration */ gl.useProgram(test.po_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() failed"); /* Draw the test geometry */ gl.beginTransformFeedback(GL_TRIANGLES); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback(GL_TRIANGLES) failed."); gl.drawArrays(m_glExtTokens.PATCHES, 0 /* first */, 1 /* count */); GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays() failed"); gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback() failed"); /* Map the BO with result data into user space */ const float* triangle_vertex_data = (const float*)gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, /* offset */ sizeof(float) * 3 /* vec3 */ * 3 /* points */, GL_MAP_READ_BIT); GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange() call failed"); /* Make sure the triangle data is correct. Since we cannot rely on any specific order * of the result vertices, raise corresponding flag for each of the expected vertices. */ const float epsilon = 1e-5f; bool is_zero_zero_one_present = false; bool is_zero_one_zero_present = false; bool is_one_zero_zero_present = false; for (unsigned int n_vertex = 0; n_vertex < 3 /* vertices */; ++n_vertex) { const float* triangle_ptr = triangle_vertex_data + 3 * n_vertex; if (de::abs(triangle_ptr[0]) < epsilon && de::abs(triangle_ptr[1]) < epsilon && de::abs(triangle_ptr[2] - 1.0f) < epsilon) { if (!is_zero_zero_one_present) { is_zero_zero_one_present = true; } else { TCU_FAIL("(0, 0, 1) vertex outputted more than once"); } } else if (de::abs(triangle_ptr[0]) < epsilon && de::abs(triangle_ptr[1] - 1.0f) < epsilon && de::abs(triangle_ptr[2]) < epsilon) { if (!is_zero_one_zero_present) { is_zero_one_zero_present = true; } else { TCU_FAIL("(0, 1, 0) vertex outputted more than once"); } } else if (de::abs(triangle_ptr[0] - 1.0f) < epsilon && de::abs(triangle_ptr[1]) < epsilon && de::abs(triangle_ptr[2]) < epsilon) { if (!is_one_zero_zero_present) { is_one_zero_zero_present = true; } else { TCU_FAIL("(1, 0, 0) vertex outputted more than once"); } } else { m_testCtx.getLog() << tcu::TestLog::Message << "Unexpected vertex" << " (" << triangle_vertex_data[0] << ", " << triangle_vertex_data[1] << ", " << triangle_vertex_data[2] << ")" << " encountered for a degenerate triangle." << tcu::TestLog::EndMessage; TCU_FAIL("Invalid vertex was generated by the tessellator"); } } /* for (all vertices) */ /* Unmap the BO */ gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER); GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer() call failed"); } /* All done */ m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); return STOP; } /** Constructor * * @param context Test context **/ TessellationShaderTrianglesIdenticalTriangles::TessellationShaderTrianglesIdenticalTriangles( Context& context, const ExtParameters& extParams) : TestCaseBase(context, extParams, "identical_triangles", "Verifies that tessellation coordinates generated by the tessellator " "running in triangles mode do not change if second inner or fourth " "outer tessellation level is changed") , m_vao_id(0) , m_utils(DE_NULL) { /* Left blank on purpose */ } /** Deinitializes ES objects created for the test. */ void TessellationShaderTrianglesIdenticalTriangles::deinit() { /* Call base class' deinit() */ TestCaseBase::deinit(); if (!m_is_tessellation_shader_supported) { return; } const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Unbind vertex array object */ gl.bindVertexArray(0); /* Deallocate test variables */ 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; } } /** Initializes ES objects necessary to run the test. */ void TessellationShaderTrianglesIdenticalTriangles::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 */ glw::GLint gl_max_tess_gen_level_value = 0; gl.getIntegerv(m_glExtTokens.MAX_TESS_GEN_LEVEL, &gl_max_tess_gen_level_value); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() call failed for GL_MAX_TESS_GEN_LEVEL_EXT pname"); /* Initialize all test runs */ _tessellation_level_set_filter filter = (_tessellation_level_set_filter)((int)TESSELLATION_LEVEL_SET_FILTER_ALL_COMBINATIONS | (int)TESSELLATION_LEVEL_SET_FILTER_EXCLUDE_NEGATIVE_BASE_VALUE); _tessellation_levels_set levels_sets = TessellationShaderUtils::getTessellationLevelSetForPrimitiveMode( TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES, gl_max_tess_gen_level_value, filter); for (_tessellation_levels_set_const_iterator set_iterator = levels_sets.begin(); set_iterator != levels_sets.end(); set_iterator++) { _run run; const _tessellation_levels& set = *set_iterator; memcpy(run.base_inner, set.inner, sizeof(run.base_inner)); memcpy(run.base_outer, set.outer, sizeof(run.base_outer)); run.reference_inner[0] = run.base_inner[0]; run.reference_inner[1] = run.base_inner[1] * 0.25f; run.reference_outer[0] = run.base_outer[0]; run.reference_outer[1] = run.base_outer[1]; run.reference_outer[2] = run.base_outer[2]; run.reference_outer[3] = run.base_outer[3] * 0.25f; /* Retrieve vertex data for both passes */ glw::GLint n_base_vertices = 0; glw::GLint n_reference_vertices = 0; n_base_vertices = m_utils->getAmountOfVerticesGeneratedByTessellator( TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES, run.base_inner, run.base_outer, TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, false); n_reference_vertices = m_utils->getAmountOfVerticesGeneratedByTessellator( TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES, run.reference_inner, run.reference_outer, TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, false); /* is_point_mode_enabled */ if (n_base_vertices == 0) { m_testCtx.getLog() << tcu::TestLog::Message << "No vertices were generated by tessellator for: " "inner tess levels:" "[" << run.base_inner[0] << ", " << run.base_inner[1] << "]" ", outer tess levels:" "[" << run.base_outer[0] << ", " << run.base_outer[1] << ", " << run.base_outer[2] << ", " << run.base_outer[3] << "]" ", primitive mode: triangles, vertex spacing: equal." << tcu::TestLog::EndMessage; TCU_FAIL("Zero vertices were generated by tessellator for base test pass"); } if (n_reference_vertices == 0) { m_testCtx.getLog() << tcu::TestLog::Message << "No vertices were generated by tessellator for: " "inner tess levels:" "[" << run.reference_inner[0] << ", " << run.reference_inner[1] << "]" ", outer tess levels:" "[" << run.reference_outer[0] << ", " << run.reference_outer[1] << ", " << run.reference_outer[2] << ", " << run.reference_outer[3] << "]" ", primitive mode: triangles, vertex spacing: equal." << tcu::TestLog::EndMessage; TCU_FAIL("Zero vertices were generated by tessellator for reference test pass"); } if (n_base_vertices != n_reference_vertices) { m_testCtx.getLog() << tcu::TestLog::Message << "Amount of vertices generated by the tessellator differs" " for the following inner/outer configs: " "inner tess levels:" "[" << run.base_inner[0] << ", " << run.base_inner[1] << "]" ", outer tess levels:" "[" << run.base_outer[0] << ", " << run.base_outer[1] << ", " << run.base_outer[2] << ", " << run.base_outer[3] << "]" " and inner tess levels:" "[" << run.reference_inner[0] << ", " << run.reference_inner[1] << "]" ", outer tess levels:" "[" << run.reference_outer[0] << ", " << run.reference_outer[1] << ", " << run.reference_outer[2] << ", " << run.reference_outer[3] << "]" ", primitive mode: triangles, vertex spacing: equal." << tcu::TestLog::EndMessage; TCU_FAIL("Amount of vertices generated by tessellator differs between base and references passes"); } run.n_vertices = n_base_vertices; run.base_data = m_utils->getDataGeneratedByTessellator( run.base_inner, false, /* is_point_mode_enabled */ TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES, TESSELLATION_SHADER_VERTEX_ORDERING_CCW, TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, run.base_outer); run.reference_data = m_utils->getDataGeneratedByTessellator( run.reference_inner, false, /* is_point_mode_enabled */ TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES, TESSELLATION_SHADER_VERTEX_ORDERING_CCW, TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, run.reference_outer); /* Store the run data */ m_runs.push_back(run); } /* for (all sets) */ } /** 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 TessellationShaderTrianglesIdenticalTriangles::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++) { const _run& run = *run_iterator; /* Make sure the vertex data generated for two passes matches */ const glw::GLint n_triangles = run.n_vertices / 3 /* vertices per triangle */; for (int n_triangle = 0; n_triangle < n_triangles; ++n_triangle) { const float* triangle_a = (const float*)(&run.base_data[0]) + n_triangle * 3 /* vertices */ * 3; /* components */ const float* triangle_b = (const float*)(&run.reference_data[0]) + n_triangle * 3 /* vertices */ * 3; /* components */ if (!TessellationShaderUtils::isTriangleDefined(triangle_a, triangle_b)) { m_testCtx.getLog() << tcu::TestLog::Message << "The following triangle, generated in the first pass, was not " "generated in the other. " "First pass' configuration: inner tess levels:" "[" << run.base_inner[0] << ", " << run.base_inner[1] << "]" ", outer tess levels:" "[" << run.base_outer[0] << ", " << run.base_outer[1] << ", " << run.base_outer[2] << ", " << run.base_outer[3] << "]" "; second pass' configuration: inner tess levels:" "[" << run.reference_inner[0] << ", " << run.reference_inner[1] << "]" ", outer tess levels:" "[" << run.reference_outer[0] << ", " << run.reference_outer[1] << ", " << run.reference_outer[2] << ", " << run.reference_outer[3] << "]" ", primitive mode: triangles, vertex spacing: equal." << tcu::TestLog::EndMessage; TCU_FAIL("A triangle from base vertex data set was not found in reference vertex data set."); } } /* for (all vertices) */ } /* for (all runs) */ /* All done */ m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); return STOP; } /** Constructor * * @param context Test context **/ TessellationShaderTrianglesInnerTessellationLevelRounding::TessellationShaderTrianglesInnerTessellationLevelRounding( Context& context, const ExtParameters& extParams) : TestCaseBase(context, extParams, "inner_tessellation_level_rounding", "Verifies that inner tessellation level is rounded to 1 or 2," " when the tessellator is run in triangles primitive mode and " "inner tessellation level is set to 1 and any of the outer " "tessellation levels is greater than one.") , m_vao_id(0) , m_utils(DE_NULL) { /* Left blank on purpose */ } /** Deinitializes ES objects created for the test. */ void TessellationShaderTrianglesInnerTessellationLevelRounding::deinit() { /* Call base class' deinit() */ TestCaseBase::deinit(); if (!m_is_tessellation_shader_supported) { return; } const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Unbind vertex array object */ gl.bindVertexArray(0); /* Deallocate test variables */ 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; } } /** 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 TessellationShaderTrianglesInnerTessellationLevelRounding::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); } const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* 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!"); /* Initialize and run test iterations. In later part, we will verify the generated data. */ runTestIterations(); /* Iterate through all runs */ for (_runs_const_iterator run_iterator = m_runs.begin(); run_iterator != m_runs.end(); run_iterator++) { const _run& run = *run_iterator; if (run.vertex_spacing == TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD && de::abs(run.set1_inner[0] - run.set2_inner[0]) > 1e-5f && de::min(run.set1_inner[0], run.set2_inner[0]) >= 1.0f) { /* In fractional_odd_spacing mode with inner level >= 1.0f, the clamped and rounded integer level is at least 3. These results in inner subdivision into at least -2=1 segment and two additional, typically shorter segments. The length of these two additional segments relative to the others will decrease monotonically with the value of -, so if different levels were used, we cannot proceed with matching the exact vertex data. */ continue; } /* Make sure the vertex data generated for two passes matches */ const glw::GLint n_triangles = run.n_vertices / 3 /* vertices per triangle */; for (int n_triangle = 0; n_triangle < n_triangles; ++n_triangle) { const float* triangle_a = (const float*)(&run.set1_data[0]) + n_triangle * 3 /* vertices */ * 3; /* components */ const float* triangle_b = (const float*)(&run.set2_data[0]) + n_triangle * 3 /* vertices */ * 3; /* components */ if (!TessellationShaderUtils::isTriangleDefined(triangle_a, triangle_b)) { std::string vs_mode_string = TessellationShaderUtils::getESTokenForVertexSpacingMode(run.vertex_spacing); m_testCtx.getLog() << tcu::TestLog::Message << "The following triangle, generated in the first pass, was not " "generated in the second one. " "First pass' configuration: inner tess levels:" "[" << run.set1_inner[0] << ", " << run.set1_inner[1] << "]" ", outer tess levels:" "[" << run.set1_outer[0] << ", " << run.set1_outer[1] << ", " << run.set1_outer[2] << ", " << run.set1_outer[3] << "]" "; second pass' configuration: inner tess levels:" "[" << run.set2_inner[0] << ", " << run.set2_inner[1] << "]" ", outer tess levels:" "[" << run.set2_outer[0] << ", " << run.set2_outer[1] << ", " << run.set2_outer[2] << ", " << run.set2_outer[3] << "]" ", primitive mode: triangles, vertex spacing: " << vs_mode_string << tcu::TestLog::EndMessage; TCU_FAIL("A triangle from first pass' data set was not found in second pass' data set."); } } /* for (all vertices) */ } /* for (all runs) */ /* All done */ m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); return STOP; } /** Runs all test iterations needed to generate data for later verification. */ void TessellationShaderTrianglesInnerTessellationLevelRounding::runTestIterations() { /* 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); /* Retrieve GL_MAX_TESS_GEN_LEVEL_EXT value */ glw::GLint gl_max_tess_gen_level_value = 0; gl.getIntegerv(m_glExtTokens.MAX_TESS_GEN_LEVEL, &gl_max_tess_gen_level_value); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() call failed for GL_MAX_TESS_GEN_LEVEL_EXT pname"); /* Initialize all test runs */ const glw::GLint tess_levels[] = { 2, gl_max_tess_gen_level_value / 2, gl_max_tess_gen_level_value }; const unsigned int n_tess_levels = sizeof(tess_levels) / sizeof(tess_levels[0]); const _tessellation_shader_vertex_spacing vs_modes[] = { TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_EVEN, TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD }; const unsigned int n_vs_modes = sizeof(vs_modes) / sizeof(vs_modes[0]); 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]; for (unsigned int n_tess_level = 0; n_tess_level < n_tess_levels; ++n_tess_level) { /* Set up the run descriptor */ glw::GLint tess_level = tess_levels[n_tess_level]; _run run; run.set1_inner[0] = 1.0f; run.set1_inner[1] = 0.0f; run.set2_inner[1] = 0.0f; TessellationShaderUtils::getTessellationLevelAfterVertexSpacing(vs_mode, 1.5f, gl_max_tess_gen_level_value, DE_NULL, /* out_clamped */ run.set2_inner); run.set1_outer[0] = (glw::GLfloat)tess_level; run.set2_outer[0] = (glw::GLfloat)tess_level; run.set1_outer[1] = (glw::GLfloat)tess_level; run.set2_outer[1] = (glw::GLfloat)tess_level; run.set1_outer[2] = (glw::GLfloat)tess_level; run.set2_outer[2] = (glw::GLfloat)tess_level; run.set1_outer[3] = (glw::GLfloat)tess_level; run.set2_outer[3] = (glw::GLfloat)tess_level; run.vertex_spacing = vs_mode; /* Retrieve vertex data for both passes */ glw::GLint n_set1_vertices = 0; glw::GLint n_set2_vertices = 0; n_set1_vertices = m_utils->getAmountOfVerticesGeneratedByTessellator( TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES, run.set1_inner, run.set1_outer, run.vertex_spacing, false); /* is_point_mode_enabled */ n_set2_vertices = m_utils->getAmountOfVerticesGeneratedByTessellator( TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES, run.set2_inner, run.set2_outer, run.vertex_spacing, false); /* is_point_mode_enabled */ if (n_set1_vertices == 0) { 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.set1_inner[0] << ", " << run.set1_inner[1] << "]" ", outer tess levels:" "[" << run.set1_outer[0] << ", " << run.set1_outer[1] << ", " << run.set1_outer[2] << ", " << run.set1_outer[3] << "]" ", primitive mode: triangles, " "vertex spacing: " << vs_mode_string << tcu::TestLog::EndMessage; TCU_FAIL("Zero vertices were generated by tessellator for first test pass"); } if (n_set2_vertices == 0) { 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.set2_inner[0] << ", " << run.set2_inner[1] << "]" ", outer tess levels:" "[" << run.set2_outer[0] << ", " << run.set2_outer[1] << ", " << run.set2_outer[2] << ", " << run.set2_outer[3] << "]" ", primitive mode: triangles, " "vertex spacing: " << vs_mode_string << tcu::TestLog::EndMessage; TCU_FAIL("Zero vertices were generated by tessellator for second test pass"); } if (n_set1_vertices != n_set2_vertices) { std::string vs_mode_string = TessellationShaderUtils::getESTokenForVertexSpacingMode(vs_mode); m_testCtx.getLog() << tcu::TestLog::Message << "Amount of vertices generated by the tessellator differs" " for the following inner/outer configs: " "inner tess levels:" "[" << run.set1_inner[0] << ", " << run.set1_inner[1] << "]" ", outer tess levels:" "[" << run.set1_outer[0] << ", " << run.set1_outer[1] << ", " << run.set1_outer[2] << ", " << run.set1_outer[3] << "]" " and inner tess levels:" "[" << run.set2_inner[0] << ", " << run.set2_inner[1] << "]" ", outer tess levels:" "[" << run.set2_outer[0] << ", " << run.set2_outer[1] << ", " << run.set2_outer[2] << ", " << run.set2_outer[3] << "]" ", primitive mode: triangles, vertex spacing: " << vs_mode_string << tcu::TestLog::EndMessage; TCU_FAIL("Amount of vertices generated by tessellator differs between base and references passes"); } run.n_vertices = n_set1_vertices; run.set1_data = m_utils->getDataGeneratedByTessellator(run.set1_inner, false, /* is_point_mode_enabled */ TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES, TESSELLATION_SHADER_VERTEX_ORDERING_CCW, run.vertex_spacing, run.set1_outer); run.set2_data = m_utils->getDataGeneratedByTessellator(run.set2_inner, false, /* is_point_mode_enabled */ TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES, TESSELLATION_SHADER_VERTEX_ORDERING_CCW, run.vertex_spacing, run.set2_outer); /* Store the run data */ m_runs.push_back(run); } /* for (all sets) */ } /* for (all vertex spacing modes) */ } } /* namespace glcts */