| /*------------------------------------------------------------------------- |
| * 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 <f> >= 1.0f, the clamped |
| and rounded integer level <n> is at least 3. |
| |
| These results in inner subdivision into at least <n>-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 <n>-<f>, so if different <f> 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 */ |