blob: 8ab8b551a34a9fd874291ad3d715131cc5a3becb [file] [log] [blame]
/*-------------------------------------------------------------------------
* 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<char> 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 <n> is two.
*/
if (n == 2)
{
n_expected_vertices++; /* degenerate vertex */
break;
}
/* If <n> 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 <n>-2 segments,
* with the <n>-1 vertices of this subdivision produced by intersecting the
* inner edge with lines perpendicular to the edge running through the <n>-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 */