| /*------------------------------------------------------------------------- |
| * 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 "esextcTessellationShaderVertexOrdering.hpp" |
| #include "esextcTessellationShaderUtils.hpp" |
| #include "gluContextInfo.hpp" |
| #include "gluDefs.hpp" |
| #include "glwEnums.hpp" |
| #include "glwFunctions.hpp" |
| #include "tcuTestLog.hpp" |
| |
| namespace glcts |
| { |
| /** Constructor |
| * |
| * @param context Test context |
| **/ |
| TessellationShaderVertexOrdering::TessellationShaderVertexOrdering(Context& context, const ExtParameters& extParams) |
| : TestCaseBase(context, extParams, "vertex_ordering", "Verifies vertex ordering property affects the tessellation" |
| " process as per extension specification") |
| , m_bo_id(0) |
| , m_fs_id(0) |
| , m_tc_id(0) |
| , m_vs_id(0) |
| , m_vao_id(0) |
| , m_utils(DE_NULL) |
| { |
| /* Left blank on purpose */ |
| } |
| |
| /** Deinitializes ES objects created for the test. */ |
| void TessellationShaderVertexOrdering::deinit() |
| { |
| /* Call base class' deinit() */ |
| TestCaseBase::deinit(); |
| |
| if (!m_is_tessellation_shader_supported) |
| { |
| return; |
| } |
| |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| /* Reset TF buffer object bindings */ |
| gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* buffer */); |
| gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* index */, 0 /* buffer */); |
| |
| /* Restore GL_PATCH_VERTICES_EXT value */ |
| gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, 3); |
| |
| /* Disable GL_RASTERIZER_DISCARD rendering mode */ |
| gl.disable(GL_RASTERIZER_DISCARD); |
| |
| /* Reset active program object */ |
| gl.useProgram(0); |
| |
| /* 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; |
| } |
| |
| /* Denitialize utils instance */ |
| if (m_utils != DE_NULL) |
| { |
| delete m_utils; |
| |
| m_utils = DE_NULL; |
| } |
| |
| /* Deinitialize all test descriptors */ |
| _test_iterations::iterator it; |
| for (it = m_tests.begin(); it != m_tests.end(); ++it) |
| { |
| deinitTestIteration(*it); |
| } |
| m_tests.clear(); |
| |
| for (it = m_tests_points.begin(); it != m_tests_points.end(); ++it) |
| { |
| deinitTestIteration(*it); |
| } |
| m_tests_points.clear(); |
| } |
| |
| /** Deinitialize all test pass-specific ES objects. |
| * |
| * @param test Descriptor of a test pass to deinitialize. |
| **/ |
| void TessellationShaderVertexOrdering::deinitTestIteration(_test_iteration& test_iteration) |
| { |
| if (test_iteration.data != DE_NULL) |
| { |
| delete[] test_iteration.data; |
| |
| test_iteration.data = DE_NULL; |
| } |
| } |
| |
| /** Initializes ES objects necessary to run the test. */ |
| void TessellationShaderVertexOrdering::initTest() |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| /* Skip if required extensions are not supported. */ |
| if (!m_is_tessellation_shader_supported) |
| { |
| throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED); |
| } |
| |
| /* 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!"); |
| |
| /* Set up patch size */ |
| gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, 1); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glPatchParameteriEXT() call failed for GL_PATCH_VERTICES_EXT pname"); |
| |
| /* Disable rasterization */ |
| gl.enable(GL_RASTERIZER_DISCARD); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glEnable(GL_RASTERIZER_DISCARD) call failed"); |
| |
| /* Initialize utils instance */ |
| m_utils = new TessellationShaderUtils(gl, this); |
| |
| /* Generate all test-wide objects needed for test execution */ |
| 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 buffer object bindings */ |
| gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_bo_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call failed"); |
| |
| gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, /* index */ |
| m_bo_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase() call 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 */ |
| std::string tc_body = |
| TessellationShaderUtils::getGenericTCCode(4, /* n_patch_vertices */ |
| false); /* should_use_glInvocationID_indexed_input */ |
| const char* tc_body_ptr = tc_body.c_str(); |
| |
| shaderSourceSpecialized(m_tc_id, 1 /* count */, &tc_body_ptr); |
| 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, 0.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) */ |
| |
| /* 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() failed for GL_MAX_TESS_GEN_LEVEL_EXT pname"); |
| |
| /* Initialize all test iterations */ |
| bool point_mode_statuses[] = { false, true }; |
| const unsigned int n_point_mode_statuses = sizeof(point_mode_statuses) / sizeof(point_mode_statuses[0]); |
| |
| const _tessellation_primitive_mode primitive_modes[] = { TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES, |
| TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS, |
| TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES }; |
| const unsigned int n_primitive_modes = sizeof(primitive_modes) / sizeof(primitive_modes[0]); |
| |
| const _tessellation_shader_vertex_ordering vertex_orderings[] = { TESSELLATION_SHADER_VERTEX_ORDERING_CCW, |
| TESSELLATION_SHADER_VERTEX_ORDERING_CW, |
| TESSELLATION_SHADER_VERTEX_ORDERING_DEFAULT }; |
| const unsigned int n_vertex_orderings = sizeof(vertex_orderings) / sizeof(vertex_orderings[0]); |
| |
| for (unsigned int n_primitive_mode = 0; n_primitive_mode < n_primitive_modes; ++n_primitive_mode) |
| { |
| _tessellation_levels_set levels_set; |
| _tessellation_primitive_mode primitive_mode = primitive_modes[n_primitive_mode]; |
| |
| levels_set = TessellationShaderUtils::getTessellationLevelSetForPrimitiveMode( |
| primitive_mode, gl_max_tess_gen_level_value, |
| TESSELLATION_LEVEL_SET_FILTER_INNER_AND_OUTER_LEVELS_USE_DIFFERENT_VALUES); |
| |
| for (unsigned int n_vertex_ordering = 0; n_vertex_ordering < n_vertex_orderings; ++n_vertex_ordering) |
| { |
| _tessellation_shader_vertex_ordering vertex_ordering = vertex_orderings[n_vertex_ordering]; |
| |
| for (_tessellation_levels_set_const_iterator levels_set_iterator = levels_set.begin(); |
| levels_set_iterator != levels_set.end(); levels_set_iterator++) |
| { |
| const _tessellation_levels& levels = *levels_set_iterator; |
| |
| for (unsigned int n_point_mode_status = 0; n_point_mode_status < n_point_mode_statuses; |
| ++n_point_mode_status) |
| { |
| bool point_mode_status = point_mode_statuses[n_point_mode_status]; |
| |
| /* Initialize a test run descriptor for the iteration-specific properties */ |
| _test_iteration test_iteration = initTestIteration(levels.inner, levels.outer, primitive_mode, |
| vertex_ordering, point_mode_status, m_utils); |
| |
| /* Store the test iteration descriptor */ |
| if (!point_mode_status) |
| { |
| m_tests.push_back(test_iteration); |
| } |
| else |
| { |
| m_tests_points.push_back(test_iteration); |
| } |
| } /* for (all point mode statuses) */ |
| } /* for (all level sets) */ |
| } /* for (all vertex orderings) */ |
| } /* for (all primitive modes) */ |
| |
| /* Set up buffer object bindings */ |
| gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_bo_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() failed"); |
| |
| gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* index */, m_bo_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase() failed"); |
| } |
| |
| /** Initializes all ES objects, runs the test, captures all vertices |
| * generated by the tessellator and stores them in the result |
| * descriptor. |
| * |
| * NOTE: This function throws a TestError exception, should an error occur. |
| * |
| * @param inner_tess_levels Two FP values describing inner tessellation level values to be used |
| * for the test run. Must not be NULL. |
| * @param outer_tess_levels Four FP values describing outer tessellation level values to be used |
| * for the test run. Must not be NULL. |
| * @param primitive_mode Primitive mode to be used for the test run. |
| * @param vertex_ordering Vertex ordering to be used for the test run. |
| * @param is_point_mode_enabled true if points mode should be used for the test run, false otherwise. |
| * |
| * @return _test_iteration instance containing all described data. |
| * |
| **/ |
| TessellationShaderVertexOrdering::_test_iteration TessellationShaderVertexOrdering::initTestIteration( |
| const float* inner_tess_levels, const float* outer_tess_levels, _tessellation_primitive_mode primitive_mode, |
| _tessellation_shader_vertex_ordering vertex_ordering, bool is_point_mode_enabled, TessellationShaderUtils* utils) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| _test_iteration test_iteration; |
| |
| /* Create & configure a tessellation evaluation shader for the iteration */ |
| const std::string te_code = TessellationShaderUtils::getGenericTECode( |
| TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, primitive_mode, vertex_ordering, is_point_mode_enabled); |
| const char* te_code_ptr = te_code.c_str(); |
| |
| glw::GLuint te_id = gl.createShader(m_glExtTokens.TESS_EVALUATION_SHADER); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() call failed for GL_TESS_EVALUATION_SHADER_EXT pname"); |
| |
| shaderSourceSpecialized(te_id, 1, /* count */ |
| &te_code_ptr); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() call failed"); |
| |
| utils->compileShaders(1, /* n_shaders */ |
| &te_id, true); /* should_succeed */ |
| |
| /* Create & form iteration-specific program object */ |
| glw::GLuint po_id = gl.createProgram(); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() call failed"); |
| |
| gl.attachShader(po_id, m_fs_id); |
| gl.attachShader(po_id, m_tc_id); |
| gl.attachShader(po_id, te_id); |
| gl.attachShader(po_id, m_vs_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader() call(s) failed"); |
| |
| /* Set up XFB */ |
| const char* varyings[] = { "result_uvw" }; |
| |
| gl.transformFeedbackVaryings(po_id, 1, /* count */ |
| varyings, GL_INTERLEAVED_ATTRIBS); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings() call failed"); |
| |
| /* Link the program object */ |
| glw::GLint link_status = GL_FALSE; |
| |
| gl.linkProgram(po_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glLinkProgram() call failed"); |
| |
| gl.getProgramiv(po_id, GL_LINK_STATUS, &link_status); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv() call failed"); |
| |
| if (link_status != GL_TRUE) |
| { |
| TCU_FAIL("Program linking failed"); |
| } |
| |
| gl.deleteShader(te_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteShader() call failed"); |
| |
| /* Fill the remaining test iteration descriptor fields */ |
| memcpy(test_iteration.inner_tess_levels, inner_tess_levels, sizeof(test_iteration.inner_tess_levels)); |
| memcpy(test_iteration.outer_tess_levels, outer_tess_levels, sizeof(test_iteration.outer_tess_levels)); |
| |
| test_iteration.is_point_mode_enabled = is_point_mode_enabled; |
| test_iteration.primitive_mode = primitive_mode; |
| test_iteration.vertex_ordering = vertex_ordering; |
| test_iteration.n_vertices = m_utils->getAmountOfVerticesGeneratedByTessellator( |
| primitive_mode, inner_tess_levels, outer_tess_levels, TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, |
| is_point_mode_enabled); |
| |
| /* Configure the buffer object storage to hold required amount of data */ |
| glw::GLuint bo_size = static_cast<glw::GLuint>(test_iteration.n_vertices * 3 /* components */ * sizeof(float)); |
| |
| gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, bo_size, DE_NULL, /* data */ |
| GL_STATIC_DRAW); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() call failed"); |
| |
| /* Also configure the storage in the descriptor */ |
| test_iteration.data = new char[bo_size]; |
| |
| /* Render the data set */ |
| glw::GLint inner_tess_level_uniform_location = gl.getUniformLocation(po_id, "inner_tess_level"); |
| glw::GLint outer_tess_level_uniform_location = gl.getUniformLocation(po_id, "outer_tess_level"); |
| glw::GLenum tf_mode = TessellationShaderUtils::getTFModeForPrimitiveMode(primitive_mode, is_point_mode_enabled); |
| |
| DE_ASSERT(inner_tess_level_uniform_location != -1); |
| DE_ASSERT(outer_tess_level_uniform_location != -1); |
| |
| gl.useProgram(po_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() call failed"); |
| |
| gl.uniform2fv(inner_tess_level_uniform_location, 1 /* count */, test_iteration.inner_tess_levels); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glUniform2fv() call failed"); |
| |
| gl.uniform4fv(outer_tess_level_uniform_location, 1 /* count */, test_iteration.outer_tess_levels); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glUniform4fv() call failed"); |
| |
| gl.beginTransformFeedback(tf_mode); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback() call failed"); |
| |
| gl.drawArrays(m_glExtTokens.PATCHES, 0 /* first */, 1 /* count */); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays() call failed"); |
| |
| gl.endTransformFeedback(); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback() call failed"); |
| |
| /* Map the XFB buffer object and copy the rendered data */ |
| const float* xfb_data = (const float*)gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, /* offset */ |
| bo_size, GL_MAP_READ_BIT); |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange() call failed"); |
| |
| memcpy(test_iteration.data, xfb_data, bo_size); |
| |
| /* Unmap the buffer object, now that we're done retrieving the captured data */ |
| gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer() call failed"); |
| |
| gl.deleteProgram(po_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteProgram() call failed"); |
| |
| return test_iteration; |
| } |
| |
| /** 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 TessellationShaderVertexOrdering::iterate(void) |
| { |
| initTest(); |
| |
| /* Do not execute if required extensions are not supported. */ |
| if (!m_is_tessellation_shader_supported) |
| { |
| throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED); |
| } |
| |
| /* There are two main separate cases to consider here: |
| * |
| * a) for runs executed in "points" mode, we need to verify that vertex |
| * ordering does not modify the order in which the points are generated. |
| * b) for both run types, for all primitives but isolines we need to verify |
| * that the vertex ordering is actually taken into account. |
| */ |
| const float epsilon = 1e-5f; |
| |
| for (_test_iterations_const_iterator test_iterator = m_tests.begin(); test_iterator != m_tests.end(); |
| test_iterator++) |
| { |
| if (test_iterator->primitive_mode != TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES) |
| { |
| verifyVertexOrderingCorrectness(*test_iterator); |
| } |
| } /* for (all non-points runs) */ |
| |
| for (_test_iterations_const_iterator test_iterator = m_tests_points.begin(); test_iterator != m_tests_points.end(); |
| test_iterator++) |
| { |
| const _test_iteration& test = *test_iterator; |
| |
| /* For points_mode checks, we need to find a corresponding cw+ccw test pairs */ |
| if (test.vertex_ordering == TESSELLATION_SHADER_VERTEX_ORDERING_CCW || |
| test.vertex_ordering == TESSELLATION_SHADER_VERTEX_ORDERING_DEFAULT) |
| { |
| /* Find a corresponding CW test descriptor */ |
| #if defined(DE_DEBUG) && !defined(DE_COVERAGE_BUILD) |
| bool has_paired_test_been_found = false; |
| #endif |
| _test_iteration paired_test; |
| |
| for (_test_iterations_const_iterator paired_test_iterator = m_tests_points.begin(); |
| paired_test_iterator != m_tests_points.end(); ++paired_test_iterator) |
| { |
| if (de::abs(paired_test_iterator->inner_tess_levels[0] - test_iterator->inner_tess_levels[0]) < |
| epsilon && |
| de::abs(paired_test_iterator->inner_tess_levels[1] - test_iterator->inner_tess_levels[1]) < |
| epsilon && |
| de::abs(paired_test_iterator->outer_tess_levels[0] - test_iterator->outer_tess_levels[0]) < |
| epsilon && |
| de::abs(paired_test_iterator->outer_tess_levels[1] - test_iterator->outer_tess_levels[1]) < |
| epsilon && |
| de::abs(paired_test_iterator->outer_tess_levels[2] - test_iterator->outer_tess_levels[2]) < |
| epsilon && |
| de::abs(paired_test_iterator->outer_tess_levels[3] - test_iterator->outer_tess_levels[3]) < |
| epsilon && |
| paired_test_iterator->n_vertices == test_iterator->n_vertices && |
| paired_test_iterator->primitive_mode == test_iterator->primitive_mode && |
| paired_test_iterator->vertex_ordering == TESSELLATION_SHADER_VERTEX_ORDERING_CW) |
| { |
| #if defined(DE_DEBUG) && !defined(DE_COVERAGE_BUILD) |
| has_paired_test_been_found = true; |
| #endif |
| paired_test = *paired_test_iterator; |
| |
| break; |
| } |
| } |
| |
| DE_ASSERT(has_paired_test_been_found); |
| |
| /* Good to call the verification routine */ |
| verifyVertexOrderingDoesNotChangeGeneratedPoints(test, paired_test); |
| } /* if (base test 's vertex ordering is CCW) */ |
| } /* for (all other runs) */ |
| |
| /* All done */ |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| return STOP; |
| } |
| |
| /** Verifies that vertex ordering in the data set stored in user-provided |
| * test iteration descriptor matches the setting that was used in the |
| * tessellation evaluation stage. |
| * |
| * @param test_iteration Test iteration descriptor |
| * |
| **/ |
| void TessellationShaderVertexOrdering::verifyVertexOrderingCorrectness(const _test_iteration& test_iteration) |
| { |
| /* Sanity check */ |
| DE_ASSERT(test_iteration.primitive_mode != TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES); |
| |
| /* Iterate through all vertices */ |
| const float epsilon = 1e-5f; |
| const unsigned int n_vertices_per_primitive = 3; |
| |
| for (unsigned int n_primitive = 0; n_primitive < test_iteration.n_vertices / n_vertices_per_primitive; |
| ++n_primitive) |
| { |
| const float* primitive_data = |
| (const float*)test_iteration.data + 3 /* components */ * n_primitive * n_vertices_per_primitive; |
| const float* primitive_vertex1_data = primitive_data; |
| const float* primitive_vertex2_data = primitive_vertex1_data + 3; /* components */ |
| const float* primitive_vertex3_data = primitive_vertex2_data + 3; /* components */ |
| |
| float cartesian_vertex_data[6] = { primitive_vertex1_data[0], primitive_vertex1_data[1], |
| primitive_vertex2_data[0], primitive_vertex2_data[1], |
| primitive_vertex3_data[0], primitive_vertex3_data[1] }; |
| |
| if (test_iteration.primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES) |
| { |
| /* Triangles are described in barycentric coordinate. Convert to |
| * cartesian coordinates before we continue with actual test. |
| */ |
| const float barycentric_vertex_data[] = { |
| primitive_vertex1_data[0], primitive_vertex1_data[1], primitive_vertex1_data[2], |
| primitive_vertex2_data[0], primitive_vertex2_data[1], primitive_vertex2_data[2], |
| primitive_vertex3_data[0], primitive_vertex3_data[1], primitive_vertex3_data[2], |
| }; |
| |
| /* Sanity checks .. */ |
| DE_UNREF(epsilon); |
| DE_ASSERT(de::abs(barycentric_vertex_data[0] + barycentric_vertex_data[1] + barycentric_vertex_data[2] - |
| 1.0f) < epsilon); |
| DE_ASSERT(de::abs(barycentric_vertex_data[3] + barycentric_vertex_data[4] + barycentric_vertex_data[5] - |
| 1.0f) < epsilon); |
| DE_ASSERT(de::abs(barycentric_vertex_data[6] + barycentric_vertex_data[7] + barycentric_vertex_data[8] - |
| 1.0f) < epsilon); |
| |
| for (unsigned int n_vertex = 0; n_vertex < 3; ++n_vertex) |
| { |
| TessellationShaderUtils::convertBarycentricCoordinatesToCartesian( |
| barycentric_vertex_data + n_vertex * 3, cartesian_vertex_data + n_vertex * 2); |
| } |
| } /* if (test_iteration.primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES) */ |
| |
| /* Compute result of eq 3.6.1 */ |
| float determinant = 0.0f; |
| |
| for (unsigned int n_vertex = 0; n_vertex < n_vertices_per_primitive; ++n_vertex) |
| { |
| int i_op_1 = (n_vertex + 1) % n_vertices_per_primitive; |
| |
| determinant += (cartesian_vertex_data[n_vertex * 2 /* components */ + 0] * |
| cartesian_vertex_data[i_op_1 * 2 /* components */ + 1] - |
| cartesian_vertex_data[i_op_1 * 2 /* components */ + 0] * |
| cartesian_vertex_data[n_vertex * 2 /* components */ + 1]); |
| } /* for (all vertices) */ |
| |
| determinant *= 0.5f; |
| |
| /* Positive determinant implies counterclockwise ordering */ |
| if (((test_iteration.vertex_ordering == TESSELLATION_SHADER_VERTEX_ORDERING_CCW || |
| test_iteration.vertex_ordering == TESSELLATION_SHADER_VERTEX_ORDERING_DEFAULT) && |
| determinant < 0.0f) || |
| (test_iteration.vertex_ordering == TESSELLATION_SHADER_VERTEX_ORDERING_CW && determinant >= 0.0f)) |
| { |
| std::string primitive_mode = |
| TessellationShaderUtils::getESTokenForPrimitiveMode(test_iteration.primitive_mode); |
| std::string vertex_ordering = |
| TessellationShaderUtils::getESTokenForVertexOrderingMode(test_iteration.vertex_ordering); |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << "For primitive mode: [" << primitive_mode.c_str() |
| << "] " |
| "and inner tessellation levels:" |
| " [" |
| << test_iteration.inner_tess_levels[0] << ", " << test_iteration.inner_tess_levels[1] |
| << "] " |
| "and outer tessellation levels:" |
| " [" |
| << test_iteration.outer_tess_levels[0] << ", " << test_iteration.outer_tess_levels[1] |
| << ", " << test_iteration.outer_tess_levels[2] << ", " |
| << test_iteration.outer_tess_levels[3] << "] " |
| << "and vertex ordering: [" << vertex_ordering.c_str() |
| << "] " |
| ", vertex orientation has been found to be incompatible with the ordering requested." |
| << tcu::TestLog::EndMessage; |
| |
| if (test_iteration.vertex_ordering == TESSELLATION_SHADER_VERTEX_ORDERING_CCW || |
| test_iteration.vertex_ordering == TESSELLATION_SHADER_VERTEX_ORDERING_DEFAULT) |
| { |
| TCU_FAIL("Counter-clockwise ordering was expected but retrieved tessellation coordinates are laid out " |
| "in clockwise order"); |
| } |
| else |
| { |
| TCU_FAIL("Clockwise ordering was expected but retrieved tessellation coordinates are laid out in " |
| "counter-clockwise order"); |
| } |
| } |
| } /* for (all triangles) */ |
| } |
| |
| /** Verifies that vertices generated by the tessellator do not differ when run for exactly |
| * the same tessellation evaluation shaders configure to run in point mode, with an exception |
| * that one invokation used CW ordering and the other one used CCW ordering. |
| * |
| * Note: this function throws a TestError exception, should an error occur. |
| * |
| * @param test_iteration_a Test iteration which was run in point mode and uses CCW vertex |
| * ordering. |
| * @param test_iteration_b Test iteration which was run in point mode and uses CW vertex |
| * ordering. |
| * |
| **/ |
| void TessellationShaderVertexOrdering::verifyVertexOrderingDoesNotChangeGeneratedPoints( |
| const _test_iteration& test_iteration_a, const _test_iteration& test_iteration_b) |
| { |
| const float epsilon = 1e-5f; |
| |
| /* Sanity checks */ |
| DE_ASSERT(test_iteration_a.is_point_mode_enabled); |
| DE_ASSERT(test_iteration_b.is_point_mode_enabled); |
| DE_ASSERT(test_iteration_a.vertex_ordering == TESSELLATION_SHADER_VERTEX_ORDERING_CCW || |
| test_iteration_a.vertex_ordering == TESSELLATION_SHADER_VERTEX_ORDERING_DEFAULT); |
| DE_ASSERT(test_iteration_b.vertex_ordering == TESSELLATION_SHADER_VERTEX_ORDERING_CW); |
| |
| /* Iterate through all points in test set A and make sure they can be found in test set B */ |
| for (unsigned int n_vertex_a = 0; n_vertex_a < test_iteration_a.n_vertices; ++n_vertex_a) |
| { |
| bool has_been_found = false; |
| const float* vertex_a_data = (const float*)test_iteration_a.data + n_vertex_a * 3 /* components */; |
| |
| for (unsigned int n_vertex_b = 0; n_vertex_b < test_iteration_b.n_vertices; ++n_vertex_b) |
| { |
| const float* vertex_b_data = (const float*)test_iteration_b.data + n_vertex_b * 3 /* components */; |
| |
| if (de::abs(vertex_a_data[0] - vertex_b_data[0]) < epsilon && |
| de::abs(vertex_a_data[1] - vertex_b_data[1]) < epsilon && |
| de::abs(vertex_a_data[2] - vertex_b_data[2]) < epsilon) |
| { |
| has_been_found = true; |
| |
| break; |
| } |
| } /* for (all B set vertices) */ |
| |
| if (!has_been_found) |
| { |
| std::string primitive_mode = |
| TessellationShaderUtils::getESTokenForPrimitiveMode(test_iteration_a.primitive_mode); |
| std::string vertex_ordering = |
| TessellationShaderUtils::getESTokenForVertexOrderingMode(test_iteration_a.vertex_ordering); |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << "For primitive mode: [" << primitive_mode.c_str() |
| << "] " |
| "and inner tessellation levels:" |
| " [" |
| << test_iteration_a.inner_tess_levels[0] << ", " << test_iteration_a.inner_tess_levels[1] |
| << "] " |
| "and outer tessellation levels:" |
| " [" |
| << test_iteration_a.outer_tess_levels[0] << ", " << test_iteration_a.outer_tess_levels[1] |
| << ", " << test_iteration_a.outer_tess_levels[2] << ", " |
| << test_iteration_a.outer_tess_levels[3] << "] " |
| << ", vertices generated for CW and CCW orientations do not match." |
| << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL("For runs in which only vertex ordering setting differs, vertex from one run was not found in the " |
| "other run."); |
| } |
| } /* for (all A set vertices) */ |
| } |
| |
| } /* namespace glcts */ |