/*------------------------------------------------------------------------- * 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 "esextcTessellationShaderPoints.hpp" #include "gluContextInfo.hpp" #include "gluDefs.hpp" #include "glwEnums.hpp" #include "glwFunctions.hpp" #include "tcuTestLog.hpp" namespace glcts { const unsigned int TessellationShaderPointsgl_PointSize::m_rt_height = 16; /* note: update shaders if you change this value */ const unsigned int TessellationShaderPointsgl_PointSize::m_rt_width = 16; /* note: update shaders if you change this value */ /** Constructor * * @param context Test context **/ TessellationShaderPointsTests::TessellationShaderPointsTests(glcts::Context& context, const ExtParameters& extParams) : TestCaseGroupBase(context, extParams, "tessellation_shader_point_mode", "Verifies point mode functionality") { /* No implementation needed */ } /** * Initializes test groups for geometry shader tests **/ void TessellationShaderPointsTests::init(void) { addChild(new glcts::TessellationShaderPointsgl_PointSize(m_context, m_extParams)); addChild(new glcts::TessellationShaderPointsVerification(m_context, m_extParams)); } /** Constructor * * @param context Test context * @param name Test case's name * @param description Test case's desricption **/ TessellationShaderPointsgl_PointSize::TessellationShaderPointsgl_PointSize(Context& context, const ExtParameters& extParams) : TestCaseBase(context, extParams, "point_rendering", "Verifies point size used to render points is taken from" " the right stage") , m_fbo_id(0) , m_to_id(0) , m_vao_id(0) { /* Left blank on purpose */ } /** Deinitializes all ES objects created for the test. */ void TessellationShaderPointsgl_PointSize::deinit() { /** Call base class' deinit() function */ TestCaseBase::deinit(); if (!m_is_tessellation_shader_supported) { return; } const glw::Functions& gl = m_context.getRenderContext().getFunctions(); if (!glu::isContextTypeES(m_context.getRenderContext().getType())) { /* Disable point size */ gl.disable(GL_PROGRAM_POINT_SIZE); } /* Reset the program object */ gl.useProgram(0); /* Revert GL_PATCH_VERTICES_EXT to default value */ gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, 3); /* Unbind vertex array object */ gl.bindVertexArray(0); /* Deinitialize test-specific objects */ for (_tests_iterator it = m_tests.begin(); it != m_tests.end(); ++it) { const _test_descriptor& test = *it; if (test.fs_id != 0) { gl.deleteShader(test.fs_id); } if (test.gs_id != 0) { gl.deleteShader(test.gs_id); } if (test.po_id != 0) { gl.deleteProgram(test.po_id); } if (test.tes_id != 0) { gl.deleteShader(test.tes_id); } if (test.tcs_id != 0) { gl.deleteShader(test.tcs_id); } if (test.vs_id != 0) { gl.deleteShader(test.vs_id); } } m_tests.clear(); if (m_fbo_id != 0) { gl.deleteFramebuffers(1, &m_fbo_id); m_fbo_id = 0; } if (m_to_id != 0) { gl.deleteTextures(1, &m_to_id); m_to_id = 0; } if (m_vao_id != 0) { gl.deleteVertexArrays(1, &m_vao_id); m_vao_id = 0; } } /** Initializes all ES objects that will be used for the test. */ void TessellationShaderPointsgl_PointSize::initTest() { /* The test should only execute if maximum point size is at least 2 */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); glw::GLint gl_max_point_size_value[2] = { 0 }; const int min_max_point_size = 2; if (!glu::isContextTypeES(m_context.getRenderContext().getType())) { gl.getIntegerv(GL_POINT_SIZE_RANGE, gl_max_point_size_value); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() for GL_POINT_SIZE_RANGE failed."); } else { gl.getIntegerv(GL_ALIASED_POINT_SIZE_RANGE, gl_max_point_size_value); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() for GL_ALIASED_POINT_SIZE_RANGE failed."); } if (gl_max_point_size_value[1] < min_max_point_size) { throw tcu::NotSupportedError("Maximum point size is lower than 2."); } /* The test requires EXT_tessellation_shader, EXT_tessellation_shader_point_size */ if (!m_is_tessellation_shader_supported || !m_is_tessellation_shader_point_size_supported) { throw tcu::NotSupportedError("At least one of the required extensions is 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!"); /* Initialize fs+gs+vs test descriptor */ if (m_is_geometry_shader_extension_supported) { _test_descriptor pass_fs_gs_tes_vs; /* Configure shader bodies */ pass_fs_gs_tes_vs.fs_body = "${VERSION}\n" "\n" "precision highp float;\n" "\n" "in vec4 color;\n" "out vec4 result;\n" "\n" "void main()\n" "{\n" " result = color;\n" "}\n"; pass_fs_gs_tes_vs.gs_body = "${VERSION}\n" "\n" "${GEOMETRY_SHADER_REQUIRE}\n" "${GEOMETRY_POINT_SIZE_REQUIRE}\n" "\n" "layout(points) in;\n" "layout(points, max_vertices=5) out;\n" "\n" "out vec4 color;\n" "\n" "void main()\n" "{\n" " const float point_dx = 2.0 / 16.0 /* rendertarget width */;\n" " const float point_dy = 2.0 / 16.0 /* rendertarget_height */;\n" "\n" /* Center */ " color = vec4(0.1, 0.2, 0.3, 0.4);\n" " gl_PointSize = 2.0;\n" " gl_Position = vec4(point_dx + 0.0, point_dy + 0.0, 0.0, 1.0);\n" " EmitVertex();\n" "\n" /* Top-left corner */ " color = vec4(0.2, 0.3, 0.4, 0.5);\n" " gl_PointSize = 2.0;\n" " gl_Position = vec4(point_dx - 1.0, -point_dy + 1.0, 0.0, 1.0);\n" " EmitVertex();\n" "\n" /* Top-right corner */ " color = vec4(0.3, 0.4, 0.5, 0.6);\n" " gl_PointSize = 2.0;\n" " gl_Position = vec4(-point_dx + 1.0, -point_dy + 1.0, 0.0, 1.0);\n" " EmitVertex();\n" "\n" /* Bottom-left corner */ " color = vec4(0.4, 0.5, 0.6, 0.7);\n" " gl_PointSize = 2.0;\n" " gl_Position = vec4(point_dx - 1.0, point_dy - 1.0, 0.0, 1.0);\n" " EmitVertex();\n" "\n" /* Bottom-right corner */ " color = vec4(0.5, 0.6, 0.7, 0.8);\n" " gl_PointSize = 2.0;\n" " gl_Position = vec4(-point_dx + 1.0, point_dy - 1.0, 0.0, 1.0);\n" " EmitVertex();\n" "\n" "}\n"; pass_fs_gs_tes_vs.tes_body = "${VERSION}\n" "\n" "${TESSELLATION_SHADER_REQUIRE}\n" "${TESSELLATION_POINT_SIZE_REQUIRE}\n" "\n" "layout(isolines, point_mode) in;\n" "\n" "void main()\n" "{\n" " gl_PointSize = 0.1;\n" "}\n"; pass_fs_gs_tes_vs.tcs_body = "${VERSION}\n" "\n" "${TESSELLATION_SHADER_REQUIRE}\n" "${TESSELLATION_POINT_SIZE_REQUIRE}\n" "\n" "layout(vertices=1) out;\n" "\n" "void main()\n" "{\n" " gl_out[gl_InvocationID].gl_Position =\n" " gl_in[gl_InvocationID].gl_Position;\n" " gl_out[gl_InvocationID].gl_PointSize =\n" " gl_in[gl_InvocationID].gl_PointSize;\n" "\n" " gl_TessLevelOuter[0] = 1.0;\n" " gl_TessLevelOuter[1] = 1.0;\n" " gl_TessLevelOuter[2] = 1.0;\n" " gl_TessLevelOuter[3] = 1.0;\n" " gl_TessLevelInner[0] = 1.0;\n" " gl_TessLevelInner[1] = 1.0;\n" "}\n"; pass_fs_gs_tes_vs.vs_body = "${VERSION}\n" "\n" "void main()\n" "{\n" " gl_PointSize = 0.01;\n" "}\n"; pass_fs_gs_tes_vs.draw_call_count = 1; /* Store the descriptor in a vector that will be used by iterate() */ m_tests.push_back(pass_fs_gs_tes_vs); } /* if (m_is_geometry_shader_extension_supported) */ /* Initialize fs+te+vs test descriptor */ if (m_is_tessellation_shader_supported) { _test_descriptor pass_fs_tes_vs; /* Configure shader bodies */ pass_fs_tes_vs.fs_body = "${VERSION}\n" "\n" "precision highp float;\n" "\n" "in vec4 result_color;\n" "out vec4 result;\n" "\n" "void main()\n" "{\n" " result = result_color;\n" "}\n"; pass_fs_tes_vs.tes_body = "${VERSION}\n" "\n" "${TESSELLATION_SHADER_REQUIRE}\n" "${TESSELLATION_POINT_SIZE_REQUIRE}\n" "\n" "layout(isolines, point_mode) in;\n" "\n" "in vec4 tcColor[];\n" "out vec4 result_color;\n" "\n" "void main()\n" "{\n" " gl_PointSize = 2.0;\n" " gl_Position = gl_in[0].gl_Position;\n" " result_color = tcColor[0];\n" "}\n"; pass_fs_tes_vs.tcs_body = "${VERSION}\n" "\n" "${TESSELLATION_SHADER_REQUIRE}\n" "${TESSELLATION_POINT_SIZE_REQUIRE}\n" "\n" "layout(vertices=1) out;\n" "\n" "in vec4 color[];\n" "out vec4 tcColor[];\n" "\n" "void main()\n" "{\n" " tcColor[gl_InvocationID] = color[gl_InvocationID];\n" " gl_out[gl_InvocationID].gl_Position =\n" " gl_in[gl_InvocationID].gl_Position;\n" " gl_out[gl_InvocationID].gl_PointSize =\n" " gl_in[gl_InvocationID].gl_PointSize;\n" "\n" " gl_TessLevelOuter[0] = 1.0;\n" " gl_TessLevelOuter[1] = 1.0;\n" " gl_TessLevelOuter[2] = 1.0;\n" " gl_TessLevelOuter[3] = 1.0;\n" " gl_TessLevelInner[0] = 1.0;\n" " gl_TessLevelInner[1] = 1.0;\n" "}\n"; pass_fs_tes_vs.vs_body = "${VERSION}\n" "\n" "out vec4 color;\n" "\n" "void main()\n" "{\n" " const float point_dx = 2.0 / 16.0 /* rendertarget width */;\n" " const float point_dy = 2.0 / 16.0 /* rendertarget_height */;\n" "\n" " gl_PointSize = 0.1;\n" "\n" " switch (gl_VertexID)\n" " {\n" " case 0:\n" " {\n" /* Center */ " color = vec4(0.1, 0.2, 0.3, 0.4);\n" " gl_Position = vec4(point_dx + 0.0, point_dy + 0.0, 0.0, 1.0);\n" "\n" " break;\n" " }\n" "\n" " case 1:\n" " {\n" /* Top-left corner */ " color = vec4(0.2, 0.3, 0.4, 0.5);\n" " gl_Position = vec4(point_dx - 1.0, -point_dy + 1.0, 0.0, 1.0);\n" "\n" " break;\n" " }\n" "\n" " case 2:\n" " {\n" /* Top-right corner */ " color = vec4(0.3, 0.4, 0.5, 0.6);\n" " gl_Position = vec4(-point_dx + 1.0, -point_dy + 1.0, 0.0, 1.0);\n" "\n" " break;\n" " }\n" "\n" " case 3:\n" " {\n" /* Bottom-left corner */ " color = vec4(0.4, 0.5, 0.6, 0.7);\n" " gl_Position = vec4(point_dx - 1.0, point_dy - 1.0, 0.0, 1.0);\n" "\n" " break;\n" " }\n" "\n" " case 4:\n" " {\n" /* Bottom-right corner */ " color = vec4(0.5, 0.6, 0.7, 0.8);\n" " gl_Position = vec4(-point_dx + 1.0, point_dy - 1.0, 0.0, 1.0);\n" "\n" " break;\n" " }\n" " }\n" "}\n"; pass_fs_tes_vs.draw_call_count = 5; /* points in total */ /* Store the descriptor in a vector that will be used by iterate() */ m_tests.push_back(pass_fs_tes_vs); } /* if (m_is_tessellation_shader_supported) */ /* Set up a color texture we will be rendering to */ gl.genTextures(1, &m_to_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenTextures() failed"); gl.bindTexture(GL_TEXTURE_2D, m_to_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTexture() failed"); gl.texStorage2D(GL_TEXTURE_2D, 1 /* levels */, GL_RGBA8, m_rt_width, m_rt_height); GLU_EXPECT_NO_ERROR(gl.getError(), "glTexStorage2D() failed"); /* Set up a FBO we'll use for rendering */ gl.genFramebuffers(1, &m_fbo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenFramebuffers() failed"); gl.bindFramebuffer(GL_FRAMEBUFFER, m_fbo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer() failed"); gl.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_to_id, 0 /* level */); GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferTexture2D() failed"); if (!glu::isContextTypeES(m_context.getRenderContext().getType())) { /* Enable point size */ gl.enable(GL_PROGRAM_POINT_SIZE); } /* We're good to execute the test! */ } /** 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 TessellationShaderPointsgl_PointSize::iterate(void) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); initTest(); gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, 1); GLU_EXPECT_NO_ERROR(gl.getError(), "glPatchParameteriEXT() failed"); /* Iterate through all test descriptors.. */ for (_tests_iterator test_iterator = m_tests.begin(); test_iterator != m_tests.end(); test_iterator++) { _test_descriptor& test = *test_iterator; /* Generate all shader objects we'll need */ if (test.fs_body != NULL) { test.fs_id = gl.createShader(GL_FRAGMENT_SHADER); } if (test.gs_body != NULL) { test.gs_id = gl.createShader(m_glExtTokens.GEOMETRY_SHADER); } if (test.tes_body != NULL) { test.tes_id = gl.createShader(m_glExtTokens.TESS_EVALUATION_SHADER); } if (test.tcs_body != NULL) { test.tcs_id = gl.createShader(m_glExtTokens.TESS_CONTROL_SHADER); } if (test.vs_body != NULL) { test.vs_id = gl.createShader(GL_VERTEX_SHADER); } GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() call(s) failed"); /* Generate a test program object before we continue */ test.po_id = gl.createProgram(); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() call failed"); bool link_success = buildProgram(test.po_id, test.fs_id, test.fs_id ? 1 : 0, &test.fs_body, test.gs_id, test.gs_id ? 1 : 0, &test.gs_body, test.tes_id, test.tes_id ? 1 : 0, &test.tes_body, test.tcs_id, test.tcs_id ? 1 : 0, &test.tcs_body, test.vs_id, test.vs_id ? 1 : 0, &test.vs_body); if (!link_success) { TCU_FAIL("Program linking failed"); } /* Prepare for rendering */ gl.clearColor(0.0f /* red */, 0.0f /* green */, 0.0f /* blue */, 0.0f /* alpha */); GLU_EXPECT_NO_ERROR(gl.getError(), "glClearColor() failed"); gl.clear(GL_COLOR_BUFFER_BIT); GLU_EXPECT_NO_ERROR(gl.getError(), "glClear(GL_COLOR_BUFFER_BIT) failed"); gl.viewport(0 /* x */, 0 /* x */, m_rt_width, m_rt_height); GLU_EXPECT_NO_ERROR(gl.getError(), "glViewport() failed"); gl.useProgram(test.po_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() failed"); /* Render */ gl.drawArrays(m_glExtTokens.PATCHES, 0 /* first */, test.draw_call_count); GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays() failed"); /* Read back the rendered data */ unsigned char buffer[m_rt_width * m_rt_height * 4 /* components */] = { 0 }; gl.readPixels(0, /* x */ 0, /* y */ m_rt_width, m_rt_height, GL_RGBA, GL_UNSIGNED_BYTE, buffer); GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels() failed"); /* Verify all 5 points were rendered correctly */ const float epsilon = (float)1.0f / 255.0f; const unsigned int pixel_size = 4; /* components, GL_UNSIGNED_BYTE */ const float point_data[] = { /* x */ /* y */ /* r */ /* g */ /* b */ /* a */ 0.5f, 0.5f, 0.1f, 0.2f, 0.3f, 0.4f, /* center */ 0.0f, 1.0f, 0.2f, 0.3f, 0.4f, 0.5f, /* top-left */ 1.0f, 1.0f, 0.3f, 0.4f, 0.5f, 0.6f, /* top-right */ 0.0f, 0.0f, 0.4f, 0.5f, 0.6f, 0.7f, /* bottom-left */ 1.0f, 0.0f, 0.5f, 0.6f, 0.7f, 0.8f /* bottom-right */ }; const unsigned int row_size = pixel_size * m_rt_width; const unsigned int n_fields_per_point = 6; const unsigned int n_points = sizeof(point_data) / sizeof(point_data[0]) / n_fields_per_point; for (unsigned int n_point = 0; n_point < n_points; ++n_point) { int x = (int)(point_data[n_point * n_fields_per_point + 0] * float(m_rt_width - 1) + 0.5f); int y = (int)(point_data[n_point * n_fields_per_point + 1] * float(m_rt_height - 1) + 0.5f); float expected_color_r = point_data[n_point * n_fields_per_point + 2]; float expected_color_g = point_data[n_point * n_fields_per_point + 3]; float expected_color_b = point_data[n_point * n_fields_per_point + 4]; float expected_color_a = point_data[n_point * n_fields_per_point + 5]; const unsigned char* rendered_color_ubyte_ptr = buffer + row_size * y + x * pixel_size; const float rendered_color_r = float(rendered_color_ubyte_ptr[0]) / 255.0f; const float rendered_color_g = float(rendered_color_ubyte_ptr[1]) / 255.0f; const float rendered_color_b = float(rendered_color_ubyte_ptr[2]) / 255.0f; const float rendered_color_a = float(rendered_color_ubyte_ptr[3]) / 255.0f; /* Compare the pixels */ if (de::abs(expected_color_r - rendered_color_r) > epsilon || de::abs(expected_color_g - rendered_color_g) > epsilon || de::abs(expected_color_b - rendered_color_b) > epsilon || de::abs(expected_color_a - rendered_color_a) > epsilon) { m_testCtx.getLog() << tcu::TestLog::Message << "Pixel data comparison failed; expected: " << "(" << expected_color_r << ", " << expected_color_g << ", " << expected_color_b << ", " << expected_color_a << ") rendered: " << "(" << rendered_color_r << ", " << rendered_color_g << ", " << rendered_color_b << ", " << rendered_color_a << ") epsilon: " << epsilon << tcu::TestLog::EndMessage; TCU_FAIL("Pixel data comparison failed"); } } /* for (all points) */ } /* for (all tests) */ /* All done */ m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); return STOP; } /** Constructor * * @param context Test context **/ TessellationShaderPointsVerification::TessellationShaderPointsVerification(Context& context, const ExtParameters& extParams) : TestCaseBase(context, extParams, "points_verification", "Verifies points generated by the tessellator unit do not duplicate " "and that their amount is correct") , m_utils(DE_NULL) , m_vao_id(0) { /* Left blank on purpose */ } /* Deinitializes all ES Instances generated for the test */ void TessellationShaderPointsVerification::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); /* Delete utils instances */ 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 TessellationShaderPointsVerification::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 and bind 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() failed for GL_MAX_TESS_GEN_LEVEL_EXT pname"); /* Initialize all test iterations */ const _tessellation_primitive_mode primitive_modes[] = { TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS, TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES, TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES }; const unsigned int n_primitive_modes = sizeof(primitive_modes) / sizeof(primitive_modes[0]); const _tessellation_shader_vertex_spacing vertex_spacings[] = { TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD, TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_EVEN, }; const unsigned int n_vertex_spacings = sizeof(vertex_spacings) / sizeof(vertex_spacings[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_spacing = 0; n_vertex_spacing < n_vertex_spacings; ++n_vertex_spacing) { _tessellation_shader_vertex_spacing vertex_spacing = vertex_spacings[n_vertex_spacing]; 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; /* Skip border cases that this test cannot handle */ if ((primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS || primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES) && vertex_spacing == TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD && (levels.inner[0] <= 1 || levels.inner[1] <= 1)) { continue; } /* Initialize a test run descriptor for the iteration-specific properties */ _run run; memcpy(run.inner, levels.inner, sizeof(run.inner)); memcpy(run.outer, levels.outer, sizeof(run.outer)); run.primitive_mode = primitive_mode; run.vertex_spacing = vertex_spacing; m_runs.push_back(run); } /* for (all level sets) */ } /* for (all vertex spacing modes) */ } /* for (all primitive modes) */ /* Initialize utils instance. */ m_utils = new TessellationShaderUtils(gl, this); } /** 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 TessellationShaderPointsVerification::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); } /* Iterate through all the test descriptors */ for (std::vector<_run>::const_iterator run_iterator = m_runs.begin(); run_iterator != m_runs.end(); run_iterator++) { const _run& run = *run_iterator; std::vector run_data; unsigned int run_n_vertices = 0; run_data = m_utils->getDataGeneratedByTessellator(run.inner, true, /* point_mode */ run.primitive_mode, TESSELLATION_SHADER_VERTEX_ORDERING_CCW, run.vertex_spacing, run.outer); run_n_vertices = m_utils->getAmountOfVerticesGeneratedByTessellator(run.primitive_mode, run.inner, run.outer, run.vertex_spacing, true); /* point_mode */ /* First, make sure a valid amount of duplicate vertices was found for a single data set */ verifyCorrectAmountOfDuplicateVertices(run, &run_data[0], run_n_vertices); /* Now, verify that amount of generated vertices is correct, given * tessellation shader stage configuration */ verifyCorrectAmountOfVertices(run, &run_data[0], run_n_vertices); } /* for (all tests) */ /* All done */ m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); return STOP; } /** Verifies that a correct amount of vertices was generated, given test iteration-specific properties. * Throws a TestError exception in if an incorrect amount of vertices was generated by the tessellator. * * @param run Run descriptor. * @param run_data Data generated for the run. * @param run_n_vertices Amount of vertices present at @param run_data. */ void TessellationShaderPointsVerification::verifyCorrectAmountOfVertices(const _run& run, const void* run_data, unsigned int run_n_vertices) { (void)run_data; const float epsilon = 1e-5f; const glw::Functions& gl = m_context.getRenderContext().getFunctions(); unsigned int n_expected_vertices = 0; float post_vs_inner_tess_levels[2] = { 0.0f }; float post_vs_outer_tess_levels[4] = { 0.0f }; /* 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"); /* Determine vertex spacing that the tessellator should have used for current primitive mode */ glw::GLfloat actual_inner_levels[2] = { 0.0f }; _tessellation_shader_vertex_spacing actual_vs_mode = run.vertex_spacing; glw::GLfloat clamped_inner_levels[2] = { 0.0f }; memcpy(actual_inner_levels, run.inner, sizeof(run.inner)); TessellationShaderUtils::getTessellationLevelAfterVertexSpacing( run.vertex_spacing, actual_inner_levels[0], gl_max_tess_gen_level_value, clamped_inner_levels + 0, DE_NULL); /* out_clamped_and_rounded */ TessellationShaderUtils::getTessellationLevelAfterVertexSpacing( run.vertex_spacing, actual_inner_levels[1], gl_max_tess_gen_level_value, clamped_inner_levels + 1, DE_NULL); /* out_clamped_and_rounded */ if (run.primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES) { /* For isolines tessellation, outer[1] is subdivided as per specified vertex spacing as specified. * outer[0] should be subdivided using equal vertex spacing. * * This is owing to the following language in the spec (* marks important subtleties): * * The *u==0* and *u==1* edges of the rectangle are subdivided according to the first outer * tessellation level. For the purposes of *this* subdivision, the tessellation spacing mode * is ignored and treated as "equal_spacing". */ TessellationShaderUtils::getTessellationLevelAfterVertexSpacing(TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, run.outer[0], gl_max_tess_gen_level_value, DE_NULL, /* out_clamped */ post_vs_outer_tess_levels); TessellationShaderUtils::getTessellationLevelAfterVertexSpacing( actual_vs_mode, run.outer[1], gl_max_tess_gen_level_value, DE_NULL, /* out_clamped */ post_vs_outer_tess_levels + 1); } if (run.primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS) { /* As per extension spec: * * if either clamped inner tessellation level is one, that tessellation level * is treated as though it were originally specified as 1+epsilon, which would * rounded up to result in a two- or three-segment subdivision according to the * tessellation spacing. * **/ if (de::abs(clamped_inner_levels[0] - 1.0f) < epsilon) { TessellationShaderUtils::getTessellationLevelAfterVertexSpacing( run.vertex_spacing, clamped_inner_levels[0] + 1.0f, /* epsilon */ gl_max_tess_gen_level_value, DE_NULL, /* out_clamped */ actual_inner_levels + 0); } if (de::abs(clamped_inner_levels[1] - 1.0f) < epsilon) { TessellationShaderUtils::getTessellationLevelAfterVertexSpacing( run.vertex_spacing, clamped_inner_levels[1] + 1.0f, /* epsilon */ gl_max_tess_gen_level_value, DE_NULL, /* out_clamped */ actual_inner_levels + 1); } } /* Retrieve tessellation level values, taking vertex spacing setting into account */ for (int n = 0; n < 2 /* inner tessellation level values */; ++n) { TessellationShaderUtils::getTessellationLevelAfterVertexSpacing( actual_vs_mode, actual_inner_levels[n], gl_max_tess_gen_level_value, DE_NULL, /* out_clamped */ post_vs_inner_tess_levels + n); } if (run.primitive_mode != TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES) { for (int n = 0; n < 4 /* outer tessellation level values */; ++n) { TessellationShaderUtils::getTessellationLevelAfterVertexSpacing( actual_vs_mode, run.outer[n], gl_max_tess_gen_level_value, DE_NULL, /* out_clamped */ post_vs_outer_tess_levels + n); } } /* Calculate amount of vertices that should be generated in point mode */ switch (run.primitive_mode) { case TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES: { n_expected_vertices = int(post_vs_outer_tess_levels[0]) * int(post_vs_outer_tess_levels[1] + 1); break; } case TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS: { n_expected_vertices = /* outer quad */ int(post_vs_outer_tess_levels[0]) + int(post_vs_outer_tess_levels[1]) + int(post_vs_outer_tess_levels[2]) + int(post_vs_outer_tess_levels[3]) + /* inner quad */ (int(post_vs_inner_tess_levels[0]) - 1) * (int(post_vs_inner_tess_levels[1]) - 1); break; } case TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES: { /* If the first inner tessellation level and all three outer tessellation * levels are exactly one after clamping and rounding, only a single triangle * with (u,v,w) coordinates of (0,0,1), (1,0,0), and (0,1,0) is generated. */ if (de::abs(run.inner[0] - 1.0f) < epsilon && de::abs(run.outer[0] - 1.0f) < epsilon && de::abs(run.outer[1] - 1.0f) < epsilon && de::abs(run.outer[2] - 1.0f) < epsilon) { n_expected_vertices = 3; } else { /* If the inner tessellation level is one and any of the outer tessellation * levels is greater than one, the inner tessellation level is treated as * though it were originally specified as 1+epsilon and will be rounded up to * result in a two- or three-segment subdivision according to the * tessellation spacing. */ if (de::abs(run.inner[0] - 1.0f) < epsilon && (run.outer[0] > 1.0f || run.outer[1] > 1.0f || run.outer[2] > 1.0f)) { TessellationShaderUtils::getTessellationLevelAfterVertexSpacing( run.vertex_spacing, 2.0f, gl_max_tess_gen_level_value, DE_NULL, /* out_clamped */ post_vs_inner_tess_levels); } /* Count vertices making up concentric inner triangles */ n_expected_vertices = (int)post_vs_outer_tess_levels[0] + (int)post_vs_outer_tess_levels[1] + (int)post_vs_outer_tess_levels[2]; for (int n = (int)post_vs_inner_tess_levels[0]; n >= 0; n -= 2) { /* For the outermost inner triangle, the inner triangle is degenerate - * a single point at the center of the triangle -- if is two. */ if (n == 2) { n_expected_vertices++; /* degenerate vertex */ break; } /* If is three, the edges of the inner triangle are not subdivided and is * the final triangle in the set of concentric triangles. */ if (n == 3) { n_expected_vertices += 3 /* vertices per triangle */; break; } /* Otherwise, each edge of the inner triangle is divided into -2 segments, * with the -1 vertices of this subdivision produced by intersecting the * inner edge with lines perpendicular to the edge running through the -1 * innermost vertices of the subdivision of the outer edge. */ if (n >= 2) { n_expected_vertices += (n - 2) * 3 /* triangle edges */; } else { /* Count in the degenerate point instead */ n_expected_vertices++; } } /* for (all inner triangles) */ } break; } default: { TCU_FAIL("Unrecognized primitive mode"); } } /* switch (test.primitive_mode) */ /* Compare two values */ if (run_n_vertices != n_expected_vertices) { std::string primitive_mode = TessellationShaderUtils::getESTokenForPrimitiveMode(run.primitive_mode); std::string vertex_spacing = TessellationShaderUtils::getESTokenForVertexSpacingMode(run.vertex_spacing); m_testCtx.getLog() << tcu::TestLog::Message << run_n_vertices << " vertices were generated by the tessellator instead of expected " << n_expected_vertices << " for primitive mode [" << primitive_mode << "], vertex spacing mode [" << vertex_spacing << "], inner tessellation levels:[" << run.inner[0] << ", " << run.inner[1] << "], outer tessellation levels:[" << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", " << run.outer[3] << "], point mode enabled." << tcu::TestLog::EndMessage; TCU_FAIL("Amount of vertices generated in point mode was incorrect"); } /* if (test.n_vertices != n_expected_vertices) */ } /** Verifies a valid amount of duplicate vertices is present in the set of coordinates * generated by the tessellator, as described by user-provided test iteration descriptor. * Throws a TestError exception if the vertex set does not meet the requirements. * * @param test Test iteration descriptor. * @param run_data Data generated for the run. * @param run_n_vertices Amount of vertices present at @param run_data. **/ void TessellationShaderPointsVerification::verifyCorrectAmountOfDuplicateVertices(const _run& run, const void* run_data, unsigned int run_n_vertices) { const float epsilon = 1e-5f; unsigned int n_duplicate_vertices = 0; for (unsigned int n_vertex_a = 0; n_vertex_a < run_n_vertices; ++n_vertex_a) { const float* vertex_a = (const float*)run_data + n_vertex_a * 3; /* components */ for (unsigned int n_vertex_b = n_vertex_a + 1; n_vertex_b < run_n_vertices; ++n_vertex_b) { const float* vertex_b = (const float*)run_data + n_vertex_b * 3; /* components */ if (de::abs(vertex_a[0] - vertex_b[0]) < epsilon && de::abs(vertex_a[1] - vertex_b[1]) < epsilon && de::abs(vertex_a[2] - vertex_b[2]) < epsilon) { n_duplicate_vertices++; } } /* for (all vertices) */ } /* for (all vertices) */ if (n_duplicate_vertices != 0) { std::string vertex_spacing = TessellationShaderUtils::getESTokenForVertexSpacingMode(run.vertex_spacing); m_testCtx.getLog() << tcu::TestLog::Message << "Duplicate vertices found for the following tesselelation" " configuration: tessellation level:" "[" << run.inner[0] << ", " << run.inner[1] << "], " "outer tessellation level:" " [" << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", " << run.outer[3] << "], " << "vertex spacing mode:[" << vertex_spacing.c_str() << "]" << tcu::TestLog::EndMessage; TCU_FAIL("Duplicate vertex found"); } } } /* namespace glcts */