| /*------------------------------------------------------------------------- |
| * 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 "esextcTessellationShaderQuads.hpp" |
| #include "gluContextInfo.hpp" |
| #include "gluDefs.hpp" |
| #include "glwEnums.hpp" |
| #include "glwFunctions.hpp" |
| #include "tcuTestLog.hpp" |
| #include <algorithm> |
| |
| namespace glcts |
| { |
| |
| /** Constructor |
| * |
| * @param context Test context |
| **/ |
| TessellationShaderQuadsTests::TessellationShaderQuadsTests(glcts::Context &context, const ExtParameters &extParams) |
| : TestCaseGroupBase(context, extParams, "tessellation_shader_quads_tessellation", |
| "Verifies quad tessellation functionality") |
| { |
| /* No implementation needed */ |
| } |
| |
| /** |
| * Initializes test groups for geometry shader tests |
| **/ |
| void TessellationShaderQuadsTests::init(void) |
| { |
| addChild(new glcts::TessellationShaderQuadsDegenerateCase(m_context, m_extParams)); |
| addChild(new glcts::TessellationShaderQuadsInnerTessellationLevelRounding(m_context, m_extParams)); |
| } |
| |
| /** Constructor |
| * |
| * @param context Test context |
| **/ |
| TessellationShaderQuadsDegenerateCase::TessellationShaderQuadsDegenerateCase(Context &context, |
| const ExtParameters &extParams) |
| : TestCaseBase(context, extParams, "degenerate_case", |
| "Verifies that only a single triangle pair covering the outer rectangle" |
| " is generated, if both clamped inner and outer tessellation levels are " |
| "set to one.") |
| , m_vao_id(0) |
| , m_utils(DE_NULL) |
| { |
| /* Left blank on purpose */ |
| } |
| |
| /** Deinitializes ES objects created for the test. */ |
| void TessellationShaderQuadsDegenerateCase::deinit() |
| { |
| const glw::Functions &gl = m_context.getRenderContext().getFunctions(); |
| |
| /* Call base class' deinit() */ |
| TestCaseBase::deinit(); |
| |
| /* Unbind vertex array object */ |
| gl.bindVertexArray(0); |
| |
| /* Deinitialize utils instance */ |
| if (m_utils != DE_NULL) |
| { |
| delete m_utils; |
| |
| m_utils = DE_NULL; |
| } |
| |
| /* Delete vertex array object */ |
| if (m_vao_id != 0) |
| { |
| gl.deleteVertexArrays(1, &m_vao_id); |
| |
| m_vao_id = 0; |
| } |
| } |
| |
| /** Initializes ES objects necessary to run the test. */ |
| void TessellationShaderQuadsDegenerateCase::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 */ |
| const glw::GLint tess_levels[] = {-gl_max_tess_gen_level_value / 2, -1, 1}; |
| const unsigned int n_tess_levels = sizeof(tess_levels) / sizeof(tess_levels[0]); |
| |
| const _tessellation_shader_vertex_spacing vs_modes[] = { |
| /* NOTE: We do not check "fractional even" vertex spacing since it will always |
| * clamp to 2 which is out of scope for this test. |
| */ |
| TESSELLATION_SHADER_VERTEX_SPACING_DEFAULT, TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, |
| TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD}; |
| const unsigned int n_vs_modes = sizeof(vs_modes) / sizeof(vs_modes[0]); |
| |
| /* Iterate through all vertex spacing modes */ |
| bool has_failed = false; |
| |
| 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]; |
| |
| /* Iterate through all values that should be used for irrelevant tessellation levels */ |
| for (unsigned int n_tess_level = 0; n_tess_level < n_tess_levels; ++n_tess_level) |
| { |
| const glw::GLint tess_level = tess_levels[n_tess_level]; |
| |
| /* Set up the run descriptor. |
| * |
| * Round outer tesellation levels to 1 if necessary, since otherwise no geometry will |
| * be generated. |
| **/ |
| _run run; |
| |
| run.inner[0] = (float)tess_level; |
| run.inner[1] = (float)tess_level; |
| run.outer[0] = (float)((tess_level < 0) ? 1 : tess_level); |
| run.outer[1] = (float)((tess_level < 0) ? 1 : tess_level); |
| run.outer[2] = (float)((tess_level < 0) ? 1 : tess_level); |
| run.outer[3] = (float)((tess_level < 0) ? 1 : tess_level); |
| run.vertex_spacing = vs_mode; |
| |
| /* Retrieve vertex data for both passes */ |
| run.n_vertices = m_utils->getAmountOfVerticesGeneratedByTessellator( |
| TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS, run.inner, run.outer, run.vertex_spacing, |
| false); /* is_point_mode_enabled */ |
| |
| if (run.n_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.inner[0] << ", " << run.inner[1] |
| << "]" |
| ", outer tess levels:" |
| "[" |
| << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", " |
| << run.outer[3] |
| << "]" |
| ", primitive mode: quads, " |
| "vertex spacing: " |
| << vs_mode_string << tcu::TestLog::EndMessage; |
| |
| has_failed = true; |
| } |
| else |
| { |
| /* Retrieve the data buffers */ |
| run.data = m_utils->getDataGeneratedByTessellator(run.inner, false, /* is_point_mode_enabled */ |
| TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS, |
| TESSELLATION_SHADER_VERTEX_ORDERING_CCW, |
| run.vertex_spacing, run.outer); |
| } |
| |
| /* Store the run data */ |
| m_runs.push_back(run); |
| } /* for (all tessellation levels) */ |
| } /* for (all vertex spacing modes) */ |
| |
| if (has_failed) |
| { |
| TCU_FAIL("Zero vertices were generated by tessellator for at least one run which is not " |
| "a correct behavior"); |
| } |
| } |
| |
| /** 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 TessellationShaderQuadsDegenerateCase::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 */ |
| |
| /* The test fails if any of the runs did not generate exactly 6 coordinates */ |
| for (_runs_const_iterator run_iterator = m_runs.begin(); run_iterator != m_runs.end(); run_iterator++) |
| { |
| const _run &run = *run_iterator; |
| |
| if (run.n_vertices != (2 /* triangles */ * 3 /* vertices */)) |
| { |
| std::string vs_mode_string = TessellationShaderUtils::getESTokenForVertexSpacingMode(run.vertex_spacing); |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << "Invalid number of coordinates (" << run.n_vertices |
| << ", instead of 6)" |
| " was generated for the following tessellation configuration: " |
| "primitive mode:quads, " |
| "vertex spacing mode:" |
| << vs_mode_string << " inner tessellation levels:" << run.inner[0] << ", " |
| << run.inner[1] << " outer tessellation levels:" << run.outer[0] << ", " << run.outer[1] |
| << ", " << run.outer[2] << ", " << run.outer[3] << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL("Invalid number of coordinates was generated for at least one run"); |
| } |
| } /* for (all runs) */ |
| |
| /* All runs should generate exactly the same set of triangles. |
| * |
| * Note: we must not assume any specific vertex ordering, so we cannot |
| * just do a plain memcmp() here. |
| */ |
| const _run &base_run = *m_runs.begin(); |
| |
| for (unsigned int n_triangle = 0; n_triangle < base_run.n_vertices / 3 /* vertices per triangle */; n_triangle++) |
| { |
| const float *base_triangle_data = (const float *)(&base_run.data[0]) + 3 /* vertices per triangle */ |
| * 3 /* components */ |
| * n_triangle; |
| |
| for (_runs_const_iterator ref_run_iterator = m_runs.begin() + 1; ref_run_iterator != m_runs.end(); |
| ref_run_iterator++) |
| { |
| const _run &ref_run = *ref_run_iterator; |
| |
| const float *ref_triangle_data1 = (const float *)(&ref_run.data[0]); |
| const float *ref_triangle_data2 = |
| (const float *)(&ref_run.data[0]) + 3 /* vertices per triangle */ * 3 /* components */; |
| |
| if (!TessellationShaderUtils::isTriangleDefined(base_triangle_data, ref_triangle_data1) && |
| !TessellationShaderUtils::isTriangleDefined(base_triangle_data, ref_triangle_data2)) |
| { |
| std::string base_vs_mode_string = |
| TessellationShaderUtils::getESTokenForVertexSpacingMode(base_run.vertex_spacing); |
| std::string ref_vs_mode_string = |
| TessellationShaderUtils::getESTokenForVertexSpacingMode(ref_run.vertex_spacing); |
| |
| m_testCtx.getLog() << tcu::TestLog::Message |
| << "Reference run does not contain a triangle found in a base run" |
| " generated for the following tessellation configuration: " |
| "primitive mode:quads, " |
| "base vertex spacing mode:" |
| << base_vs_mode_string << " base inner tessellation levels:" << base_run.inner[0] |
| << ", " << base_run.inner[1] |
| << " base outer tessellation levels:" << base_run.outer[0] << ", " |
| << base_run.outer[1] << ", " << base_run.outer[2] << ", " << base_run.outer[3] |
| << "reference vertex spacing mode:" << ref_vs_mode_string |
| << " reference inner tessellation levels:" << ref_run.inner[0] << ", " |
| << ref_run.inner[1] << " reference outer tessellation levels:" << ref_run.outer[0] |
| << ", " << ref_run.outer[1] << ", " << ref_run.outer[2] << ", " << ref_run.outer[3] |
| << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL("Reference run does not contain a triangle found in a base run"); |
| } |
| } /* for (all reference runs) */ |
| } /* for (all triangles) */ |
| |
| /* All done */ |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| return STOP; |
| } |
| |
| /** Constructor |
| * |
| * @param context Test context |
| **/ |
| TessellationShaderQuadsInnerTessellationLevelRounding::TessellationShaderQuadsInnerTessellationLevelRounding( |
| Context &context, const ExtParameters &extParams) |
| : TestCaseBase(context, extParams, "inner_tessellation_level_rounding", |
| "Verifies that either inner tessellation level is rounded to 2 or 3," |
| " when the tessellator is run in quads primitive mode and " |
| "corresponding inner tessellation level is set to 1.") |
| , m_vao_id(0) |
| , m_utils(DE_NULL) |
| { |
| /* Left blank on purpose */ |
| } |
| |
| /** Deinitializes ES objects created for the test. */ |
| void TessellationShaderQuadsInnerTessellationLevelRounding::deinit() |
| { |
| const glw::Functions &gl = m_context.getRenderContext().getFunctions(); |
| |
| /* Call base class' deinit() */ |
| TestCaseBase::deinit(); |
| |
| /* Unbind vertex array object */ |
| gl.bindVertexArray(0); |
| |
| /* Deinitialize utils instance */ |
| if (m_utils != DE_NULL) |
| { |
| delete m_utils; |
| |
| m_utils = DE_NULL; |
| } |
| |
| /* Delete vertex array object */ |
| if (m_vao_id != 0) |
| { |
| gl.deleteVertexArrays(1, &m_vao_id); |
| |
| m_vao_id = 0; |
| } |
| } |
| |
| /** Takes a vertex data set and returns a vector of unique vec2s found in the set. |
| * |
| * @param raw_data Vertex data set to process. |
| * @param n_raw_data_vertices Amount of 3-component vertices found under @param raw_data. |
| * |
| * @return As per description. |
| **/ |
| std::vector<_vec2> TessellationShaderQuadsInnerTessellationLevelRounding::getUniqueTessCoordinatesFromVertexDataSet( |
| const float *raw_data, const unsigned int n_raw_data_vertices) |
| { |
| std::vector<_vec2> result; |
| |
| for (unsigned int n_vertex = 0; n_vertex < n_raw_data_vertices; n_vertex += 2) |
| { |
| const float *vertex_data = raw_data + n_vertex * 3 /* components */; |
| _vec2 vertex(vertex_data[0], vertex_data[1]); |
| |
| if (std::find(result.begin(), result.end(), vertex) == result.end()) |
| { |
| result.push_back(vertex); |
| } |
| |
| } /* for (all vertices) */ |
| |
| return result; |
| } |
| |
| /** Initializes ES objects necessary to run the test. */ |
| void TessellationShaderQuadsInnerTessellationLevelRounding::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 */ |
| const glw::GLint tess_levels[] = {2, gl_max_tess_gen_level_value / 2, gl_max_tess_gen_level_value}; |
| |
| const glw::GLint tess_levels_odd[] = {2 + 1, gl_max_tess_gen_level_value / 2 + 1, gl_max_tess_gen_level_value - 1}; |
| 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]); |
| |
| /* Iterate through all vertex spacing modes */ |
| 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]; |
| |
| /* Iterate through all values that should be used for irrelevant tessellation levels */ |
| for (unsigned int n_tess_level = 0; n_tess_level < n_tess_levels; ++n_tess_level) |
| { |
| /* We need to test two cases in this test: |
| * |
| * a) inner[0] is set to 1 for set A and, for set B, to the value we're expecting the level to |
| * round, given the vertex spacing mode. inner[1] is irrelevant. |
| * b) inner[0] is irrelevant. inner[1] is set to 1 for set A and, for set B, to the value we're |
| * expecting the level to round, given the vertex spacing mode. |
| */ |
| for (unsigned int n_inner_tess_level_combination = 0; |
| n_inner_tess_level_combination < 2; /* inner[0], inner[1] */ |
| ++n_inner_tess_level_combination) |
| { |
| /* Set up the run descriptor */ |
| glw::GLint tess_level = (vs_mode == TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD) ? |
| tess_levels_odd[n_tess_level] : |
| tess_levels[n_tess_level]; |
| _run run; |
| |
| /* Determine inner tessellation level values for two cases */ |
| switch (n_inner_tess_level_combination) |
| { |
| case 0: |
| { |
| run.set1_inner[0] = 1.0f; |
| run.set1_inner[1] = (glw::GLfloat)tess_level; |
| run.set2_inner[1] = (glw::GLfloat)tess_level; |
| |
| TessellationShaderUtils::getTessellationLevelAfterVertexSpacing( |
| vs_mode, run.set1_inner[0] + 1.0f /* epsilon */, gl_max_tess_gen_level_value, |
| DE_NULL, /* out_clamped */ |
| run.set2_inner); |
| break; |
| } /* case 0: */ |
| |
| case 1: |
| { |
| run.set1_inner[0] = (glw::GLfloat)tess_level; |
| run.set1_inner[1] = 1.0f; |
| run.set2_inner[0] = (glw::GLfloat)tess_level; |
| |
| TessellationShaderUtils::getTessellationLevelAfterVertexSpacing( |
| vs_mode, run.set1_inner[1] + 1.0f /* epsilon */, gl_max_tess_gen_level_value, |
| DE_NULL, /* out_clamped */ |
| run.set2_inner + 1); |
| break; |
| } /* case 1: */ |
| |
| default: |
| TCU_FAIL("Invalid inner tessellation level combination index"); |
| } /* switch (n_inner_tess_level_combination) */ |
| |
| /* Configure outer tessellation level values */ |
| 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; |
| |
| /* Set up remaining run properties */ |
| 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_QUADS, run.set1_inner, run.set1_outer, run.vertex_spacing, |
| false); /* is_point_mode_enabled */ |
| n_set2_vertices = m_utils->getAmountOfVerticesGeneratedByTessellator( |
| TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS, 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: quads, " |
| "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: quads, " |
| "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: quads, vertex spacing: " |
| << vs_mode_string << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL("Amount of vertices generated by tessellator differs between base and references passes"); |
| } |
| |
| /* Store the amount of vertices in run properties */ |
| run.n_vertices = n_set1_vertices; |
| |
| /* Retrieve the data buffers */ |
| run.set1_data = m_utils->getDataGeneratedByTessellator( |
| run.set1_inner, false, /* is_point_mode_enabled */ |
| TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS, 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_QUADS, TESSELLATION_SHADER_VERTEX_ORDERING_CCW, |
| run.vertex_spacing, run.set2_outer); |
| |
| /* Store the run data */ |
| m_runs.push_back(run); |
| } /* for (all inner tess level combinations) */ |
| } /* for (all sets) */ |
| } /* for (all vertex spacing modes) */ |
| } |
| |
| /** 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 TessellationShaderQuadsInnerTessellationLevelRounding::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; |
| |
| if (run.vertex_spacing != TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD) |
| { |
| /* Make sure the vertex data generated for two passes matches */ |
| const glw::GLint n_triangles = run.n_vertices / 3 /* vertices per triangle */; |
| |
| std::vector<bool> triangleMatched; |
| triangleMatched.assign(n_triangles, false); |
| |
| 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 */ |
| /* Look up matching triangle */ |
| bool triangleFound = false; |
| |
| for (int n_triangle_b = 0; n_triangle_b < n_triangles; ++n_triangle_b) |
| { |
| if (!triangleMatched[n_triangle_b]) |
| { |
| const float *triangle_b = (const float *)(&run.set2_data[0]) + n_triangle_b * 3 /* vertices */ |
| * 3; /* components */ |
| |
| if (TessellationShaderUtils::isTriangleDefined(triangle_a, triangle_b)) |
| { |
| triangleMatched[n_triangle_b] = true; |
| triangleFound = true; |
| break; |
| } |
| } |
| } |
| |
| if (!triangleFound) |
| { |
| 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: quads, 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) */ |
| } /* if (run.vertex_spacing != TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD) */ |
| else |
| { |
| /* For Fractional Odd vertex spacing, we cannot apply the above methodology because of the fact |
| * two of the inner edges (the ones subdivided with tessellation level of 1.0) will have been |
| * subdivided differently between two runs, causing the vertex positions to shift, ultimately |
| * leading to isTriangleDefined() failure. |
| * |
| * Hence, we need to take a bit different approach for this case. If you look at a visualization |
| * of a tessellated quad, for which one of the inner tess levels has been set to 1 (let's assume |
| * inner[0] for the sake of discussion), and then looked at a list of points that were generated by the |
| * tessellator, you'll notice that even though it looks like we only have one span of triangles |
| * horizontally, there are actually three. The two outermost spans on each side (the "outer |
| * tessellation regions") are actually degenerate, because they have epsilon spacing allocated to |
| * them. |
| * |
| * Using the theory above, we look for a so-called "marker triangle". In the inner[0] = 1.0 case, |
| * it's one of the two triangles, one edge of which spans horizontally across the whole domain, and |
| * the other two edges touch the outermost edge of the quad. In the inner[1] = 1.0 case, Xs are flipped |
| * with Ys, but the general idea stays the same. |
| * |
| * So for fractional odd vertex spacing, this test verifies that the two marker triangles capping the |
| * opposite ends of the inner quad tessellation region exist. |
| */ |
| |
| /* Convert arrayed triangle-based representation to a vector storing unique tessellation |
| * coordinates. |
| */ |
| std::vector<_vec2> set1_tess_coordinates = |
| getUniqueTessCoordinatesFromVertexDataSet((const float *)(&run.set1_data[0]), run.n_vertices); |
| |
| /* Extract and sort the coordinate components we have from |
| * the minimum to the maximum */ |
| std::vector<float> set1_tess_coordinates_x_sorted; |
| std::vector<float> set1_tess_coordinates_y_sorted; |
| |
| for (std::vector<_vec2>::iterator coordinate_iterator = set1_tess_coordinates.begin(); |
| coordinate_iterator != set1_tess_coordinates.end(); coordinate_iterator++) |
| { |
| const _vec2 &coordinate = *coordinate_iterator; |
| |
| if (std::find(set1_tess_coordinates_x_sorted.begin(), set1_tess_coordinates_x_sorted.end(), |
| coordinate.x) == set1_tess_coordinates_x_sorted.end()) |
| { |
| set1_tess_coordinates_x_sorted.push_back(coordinate.x); |
| } |
| |
| if (std::find(set1_tess_coordinates_y_sorted.begin(), set1_tess_coordinates_y_sorted.end(), |
| coordinate.y) == set1_tess_coordinates_y_sorted.end()) |
| { |
| set1_tess_coordinates_y_sorted.push_back(coordinate.y); |
| } |
| } /* for (all tessellation coordinates) */ |
| |
| std::sort(set1_tess_coordinates_x_sorted.begin(), set1_tess_coordinates_x_sorted.end()); |
| std::sort(set1_tess_coordinates_y_sorted.begin(), set1_tess_coordinates_y_sorted.end()); |
| |
| /* Sanity checks */ |
| DE_ASSERT(set1_tess_coordinates_x_sorted.size() > 2); |
| DE_ASSERT(set1_tess_coordinates_y_sorted.size() > 2); |
| |
| /* NOTE: This code could have been merged for both cases at the expense of code readability. */ |
| if (run.set1_inner[0] == 1.0f) |
| { |
| /* Look for the second horizontal line segment, starting from top and bottom */ |
| const float second_y_from_top = set1_tess_coordinates_y_sorted[1]; |
| const float second_y_from_bottom = |
| set1_tess_coordinates_y_sorted[set1_tess_coordinates_y_sorted.size() - 2]; |
| |
| /* In this particular case, there should be exactly one triangle spanning |
| * from U=0 to U=1 at both these heights, with the third coordinate located |
| * at the "outer" edge. |
| */ |
| for (int n = 0; n < 2 /* cases */; ++n) |
| { |
| float y1_y2 = 0.0f; |
| float y3 = 0.0f; |
| |
| if (n == 0) |
| { |
| y1_y2 = second_y_from_bottom; |
| y3 = 1.0f; |
| } |
| else |
| { |
| y1_y2 = second_y_from_top; |
| y3 = 0.0f; |
| } |
| |
| /* Try to find the triangle */ |
| bool has_found_triangle = false; |
| |
| DE_ASSERT((run.n_vertices % 3) == 0); |
| |
| for (unsigned int n_triangle = 0; n_triangle < run.n_vertices / 3; ++n_triangle) |
| { |
| const float *vertex1_data = (const float *)(&run.set1_data[0]) + 3 /* vertices */ |
| * 3 /* components */ |
| * n_triangle; |
| const float *vertex2_data = vertex1_data + 3 /* components */; |
| const float *vertex3_data = vertex2_data + 3 /* components */; |
| |
| /* Make sure at least two Y coordinates are equal to y1_y2. */ |
| const float *y1_vertex_data = DE_NULL; |
| const float *y2_vertex_data = DE_NULL; |
| const float *y3_vertex_data = DE_NULL; |
| |
| if (vertex1_data[1] == y1_y2) |
| { |
| if (vertex2_data[1] == y1_y2 && vertex3_data[1] == y3) |
| { |
| y1_vertex_data = vertex1_data; |
| y2_vertex_data = vertex2_data; |
| y3_vertex_data = vertex3_data; |
| } |
| else if (vertex2_data[1] == y3 && vertex3_data[1] == y1_y2) |
| { |
| y1_vertex_data = vertex1_data; |
| y2_vertex_data = vertex3_data; |
| y3_vertex_data = vertex2_data; |
| } |
| } |
| else if (vertex2_data[1] == y1_y2 && vertex3_data[1] == y1_y2 && vertex1_data[1] == y3) |
| { |
| y1_vertex_data = vertex2_data; |
| y2_vertex_data = vertex3_data; |
| y3_vertex_data = vertex1_data; |
| } |
| |
| if (y1_vertex_data != DE_NULL && y2_vertex_data != DE_NULL && y3_vertex_data != DE_NULL) |
| { |
| /* Vertex 1 and 2 should span across whole domain horizontally */ |
| if ((y1_vertex_data[0] == 0.0f && y2_vertex_data[0] == 1.0f) || |
| (y1_vertex_data[0] == 1.0f && y2_vertex_data[0] == 0.0f)) |
| { |
| has_found_triangle = true; |
| |
| break; |
| } |
| } |
| } /* for (all triangles) */ |
| |
| if (!has_found_triangle) |
| { |
| TCU_FAIL("Could not find a marker triangle"); |
| } |
| } /* for (both cases) */ |
| } /* if (run.set1_inner[0] == 1.0f) */ |
| else |
| { |
| DE_ASSERT(run.set1_inner[1] == 1.0f); |
| |
| /* Look for the second vertical line segment, starting from left and right */ |
| const float second_x_from_left = set1_tess_coordinates_x_sorted[1]; |
| const float second_x_from_right = |
| set1_tess_coordinates_x_sorted[set1_tess_coordinates_x_sorted.size() - 2]; |
| |
| /* In this particular case, there should be exactly one triangle spanning |
| * from V=0 to V=1 at both these widths, with the third coordinate located |
| * at the "outer" edge. |
| */ |
| for (int n = 0; n < 2 /* cases */; ++n) |
| { |
| float x1_x2 = 0.0f; |
| float x3 = 0.0f; |
| |
| if (n == 0) |
| { |
| x1_x2 = second_x_from_right; |
| x3 = 1.0f; |
| } |
| else |
| { |
| x1_x2 = second_x_from_left; |
| x3 = 0.0f; |
| } |
| |
| /* Try to find the triangle */ |
| bool has_found_triangle = false; |
| |
| DE_ASSERT((run.n_vertices % 3) == 0); |
| |
| for (unsigned int n_triangle = 0; n_triangle < run.n_vertices / 3; ++n_triangle) |
| { |
| const float *vertex1_data = |
| (const float *)(&run.set1_data[0]) + 3 /* vertices */ * 3 /* components */ * n_triangle; |
| const float *vertex2_data = vertex1_data + 3 /* components */; |
| const float *vertex3_data = vertex2_data + 3 /* components */; |
| |
| /* Make sure at least two X coordinates are equal to x1_x2. */ |
| const float *x1_vertex_data = DE_NULL; |
| const float *x2_vertex_data = DE_NULL; |
| const float *x3_vertex_data = DE_NULL; |
| |
| if (vertex1_data[0] == x1_x2) |
| { |
| if (vertex2_data[0] == x1_x2 && vertex3_data[0] == x3) |
| { |
| x1_vertex_data = vertex1_data; |
| x2_vertex_data = vertex2_data; |
| x3_vertex_data = vertex3_data; |
| } |
| else if (vertex2_data[0] == x3 && vertex3_data[0] == x1_x2) |
| { |
| x1_vertex_data = vertex1_data; |
| x2_vertex_data = vertex3_data; |
| x3_vertex_data = vertex2_data; |
| } |
| } |
| else if (vertex2_data[0] == x1_x2 && vertex3_data[0] == x1_x2 && vertex1_data[0] == x3) |
| { |
| x1_vertex_data = vertex2_data; |
| x2_vertex_data = vertex3_data; |
| x3_vertex_data = vertex1_data; |
| } |
| |
| if (x1_vertex_data != DE_NULL && x2_vertex_data != DE_NULL && x3_vertex_data != DE_NULL) |
| { |
| /* Vertex 1 and 2 should span across whole domain vertically */ |
| if ((x1_vertex_data[1] == 0.0f && x2_vertex_data[1] == 1.0f) || |
| (x1_vertex_data[1] == 1.0f && x2_vertex_data[1] == 0.0f)) |
| { |
| has_found_triangle = true; |
| |
| break; |
| } |
| } |
| } /* for (all triangles) */ |
| |
| if (!has_found_triangle) |
| { |
| TCU_FAIL("Could not find a marker triangle (implies invalid quad tessellation for the case " |
| "considered)"); |
| } |
| } /* for (both cases) */ |
| } |
| } |
| } /* for (all runs) */ |
| |
| /* All done */ |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| return STOP; |
| } |
| |
| } /* namespace glcts */ |