blob: c0243a3bcd9795da1ecc38e847d0589a6102d6a0 [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 "esextcTessellationShaderTriangles.hpp"
#include "gluContextInfo.hpp"
#include "gluDefs.hpp"
#include "glwEnums.hpp"
#include "glwFunctions.hpp"
#include "tcuTestLog.hpp"
namespace glcts
{
/** Constructor
*
* @param context Test context
**/
TessellationShaderTrianglesTests::TessellationShaderTrianglesTests(glcts::Context& context,
const ExtParameters& extParams)
: TestCaseGroupBase(context, extParams, "tessellation_shader_triangles_tessellation",
"Verifies triangle tessellation functionality")
{
/* No implementation needed */
}
/**
* Initializes test groups for geometry shader tests
**/
void TessellationShaderTrianglesTests::init(void)
{
addChild(new glcts::TessellationShaderTrianglesDegenerateTriangle(m_context, m_extParams));
addChild(new glcts::TessellationShaderTrianglesIdenticalTriangles(m_context, m_extParams));
addChild(new glcts::TessellationShaderTrianglesInnerTessellationLevelRounding(m_context, m_extParams));
}
/** Constructor
*
* @param context Test context
**/
TessellationShaderTrianglesDegenerateTriangle::TessellationShaderTrianglesDegenerateTriangle(
Context& context, const ExtParameters& extParams)
: TestCaseBase(context, extParams, "degenerate_triangle",
"Verifies a degenerate triangle is generated by tessellator "
"under a specific configuration of inner/outer tessellation "
"levels & vertex spacing modes.")
, m_bo_id(0)
, m_fs_id(0)
, m_tc_id(0)
, m_vs_id(0)
, m_vao_id(0)
{
/* Left blank on purpose */
}
/** Deinitializes ES objects created for the test. */
void TessellationShaderTrianglesDegenerateTriangle::deinit()
{
/* Call base class' deinit() */
TestCaseBase::deinit();
if (!m_is_tessellation_shader_supported)
{
return;
}
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Deinitialize TF buffer object bindings */
gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* buffer */);
gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* index */, 0 /* buffer */);
/* Reset GL_PATCH_VERTICES_EXT value */
gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, 3);
/* Unbind vertex array object */
gl.bindVertexArray(0);
/* Free all ES objects we allocated for the test */
if (m_bo_id != 0)
{
gl.deleteBuffers(1, &m_bo_id);
m_bo_id = 0;
}
if (m_fs_id != 0)
{
gl.deleteShader(m_fs_id);
m_fs_id = 0;
}
if (m_tc_id != 0)
{
gl.deleteShader(m_tc_id);
m_tc_id = 0;
}
if (m_vs_id != 0)
{
gl.deleteShader(m_vs_id);
m_vs_id = 0;
}
if (m_vao_id != 0)
{
gl.deleteVertexArrays(1, &m_vao_id);
m_vao_id = 0;
}
/* Deinitialize all test descriptors */
for (_tests::iterator it = m_tests.begin(); it != m_tests.end(); ++it)
{
deinitTestDescriptor(*it);
}
m_tests.clear();
}
/** Deinitialize all test pass-specific ES objects.
*
* @param test Descriptor of a test pass to deinitialize.
**/
void TessellationShaderTrianglesDegenerateTriangle::deinitTestDescriptor(_test_descriptor& test)
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
if (test.po_id != 0)
{
gl.deleteProgram(test.po_id);
test.po_id = 0;
}
if (test.te_id != 0)
{
gl.deleteShader(test.te_id);
test.te_id = 0;
}
}
/** Initializes ES objects necessary to run the test. */
void TessellationShaderTrianglesDegenerateTriangle::initTest()
{
/* Skip if required extensions are not supported. */
if (!m_is_tessellation_shader_supported)
{
throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED);
}
/* Generate all test-wide objects needed for test execution */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
gl.genVertexArrays(1, &m_vao_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not generate vertex array object");
gl.bindVertexArray(m_vao_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding vertex array object!");
gl.genBuffers(1, &m_bo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers() failed");
m_fs_id = gl.createShader(GL_FRAGMENT_SHADER);
m_tc_id = gl.createShader(m_glExtTokens.TESS_CONTROL_SHADER);
m_vs_id = gl.createShader(GL_VERTEX_SHADER);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() failed");
/* Configure fragment shader body */
const char* fs_body = "${VERSION}\n"
"\n"
"void main()\n"
"{\n"
"}\n";
shaderSourceSpecialized(m_fs_id, 1 /* count */, &fs_body);
GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for fragment shader");
/* Configure tessellation control shader body */
const char* tc_body = "${VERSION}\n"
"\n"
"${TESSELLATION_SHADER_REQUIRE}\n"
"\n"
"layout (vertices=3) out;\n"
"\n"
"void main()\n"
"{\n"
" gl_out [gl_InvocationID].gl_Position = gl_in[0].gl_Position;\n"
"\n"
" gl_TessLevelInner[0] = 1.0;\n"
" gl_TessLevelOuter[0] = 1.0;\n"
" gl_TessLevelOuter[1] = 1.0;\n"
" gl_TessLevelOuter[2] = 1.0;\n"
"}\n";
shaderSourceSpecialized(m_tc_id, 1 /* count */, &tc_body);
GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for tessellation control shader");
/* Configure vertex shader body */
const char* vs_body = "${VERSION}\n"
"\n"
"void main()\n"
"{\n"
" gl_Position = vec4(1.0, 0.0, 0.0, 1.0);\n"
"}\n";
shaderSourceSpecialized(m_vs_id, 1 /* count */, &vs_body);
GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for vertex shader");
/* Compile all the shaders */
const glw::GLuint shaders[] = { m_fs_id, m_tc_id, m_vs_id };
const unsigned int n_shaders = sizeof(shaders) / sizeof(shaders[0]);
for (unsigned int n_shader = 0; n_shader < n_shaders; ++n_shader)
{
glw::GLuint shader = shaders[n_shader];
if (shader != 0)
{
glw::GLint compile_status = GL_FALSE;
gl.compileShader(shader);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader() failed");
gl.getShaderiv(shader, GL_COMPILE_STATUS, &compile_status);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv() failed");
if (compile_status != GL_TRUE)
{
TCU_FAIL("Shader compilation failed");
}
}
} /* for (all shaders) */
/* Initialize all test passes */
_test_descriptor test_equal_spacing;
_test_descriptor test_fractional_odd_spacing;
initTestDescriptor(test_equal_spacing, TESSELLATION_SHADER_VERTEX_SPACING_EQUAL);
initTestDescriptor(test_fractional_odd_spacing, TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD);
m_tests.push_back(test_equal_spacing);
m_tests.push_back(test_fractional_odd_spacing);
/* Set up buffer object storage */
gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_bo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() failed");
gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, sizeof(float) * 3 /* components */ * 3 /* UVW sets */, NULL, /* data */
GL_STATIC_DRAW);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() failed");
/* Bind the buffer object to indiced TF binding point */
gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* index */, m_bo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase() failed");
}
/** Initializes all ES objects necessary to run a specific test pass.
*
* @param test Test descriptor to fill with IDs of initialized objects.
* @param vertex_spacing Vertex spacing mode to use for the run.
**/
void TessellationShaderTrianglesDegenerateTriangle::initTestDescriptor(
_test_descriptor& test, _tessellation_shader_vertex_spacing vertex_spacing)
{
test.vertex_spacing = vertex_spacing;
/* Set up a program object for the descriptor */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
test.po_id = gl.createProgram();
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() failed");
/* Set up a pass-specific tessellation evaluation shader object. */
test.te_id = gl.createShader(m_glExtTokens.TESS_EVALUATION_SHADER);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() failed");
/* Configure tessellation evaluation shader body */
const char* te_template = "${VERSION}\n"
"\n"
"${TESSELLATION_SHADER_REQUIRE}\n"
"\n"
"layout (triangles, VERTEX_SPACING_MODE) in;\n"
"\n"
"out vec3 result_uvw;\n"
"\n"
"void main()\n"
"{\n"
" gl_Position = gl_in[0].gl_Position;\n"
" result_uvw = gl_TessCoord;\n"
"}\n";
const char* te_body_raw_ptr = DE_NULL;
std::string te_body_string = te_template;
std::string vertex_spacing_mode_string;
const char* vertex_spacing_token = "VERTEX_SPACING_MODE";
std::size_t vertex_spacing_token_index = std::string::npos;
switch (vertex_spacing)
{
case TESSELLATION_SHADER_VERTEX_SPACING_EQUAL:
{
vertex_spacing_mode_string = "equal_spacing";
break;
}
case TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD:
{
vertex_spacing_mode_string = "fractional_odd_spacing";
break;
}
default:
{
TCU_FAIL("Invalid vertex spacing mode requested");
}
}
while ((vertex_spacing_token_index = te_body_string.find(vertex_spacing_token)) != std::string::npos)
{
te_body_string = te_body_string.replace(vertex_spacing_token_index, strlen(vertex_spacing_token),
vertex_spacing_mode_string);
vertex_spacing_token_index = te_body_string.find(vertex_spacing_token);
}
te_body_raw_ptr = te_body_string.c_str();
shaderSourceSpecialized(test.te_id, 1 /* count */, &te_body_raw_ptr);
GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for tessellation evaluation shader");
/* Compile the tessellation evaluation shader */
glw::GLint compile_status = GL_FALSE;
gl.compileShader(test.te_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader() failed for tessellation evaluation shader");
gl.getShaderiv(test.te_id, GL_COMPILE_STATUS, &compile_status);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv() failed for tessellation evaluation shader");
if (compile_status != GL_TRUE)
{
TCU_FAIL("Tessellation evaluation shader compilation failed");
}
/* Attach all shader to the program object */
gl.attachShader(test.po_id, m_fs_id);
gl.attachShader(test.po_id, m_tc_id);
gl.attachShader(test.po_id, test.te_id);
gl.attachShader(test.po_id, m_vs_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader() failed");
/* Set up XFB */
const char* varyings[] = { "result_uvw" };
const unsigned int n_varyings = sizeof(varyings) / sizeof(varyings[0]);
gl.transformFeedbackVaryings(test.po_id, n_varyings, varyings, GL_INTERLEAVED_ATTRIBS);
GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings() failed");
/* Link the program object */
glw::GLint link_status = GL_FALSE;
gl.linkProgram(test.po_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glLinkProgram() failed");
gl.getProgramiv(test.po_id, GL_LINK_STATUS, &link_status);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv() failed");
if (link_status != GL_TRUE)
{
TCU_FAIL("Program linking failed");
}
}
/** Executes the test.
*
* Sets the test result to QP_TEST_RESULT_FAIL if the test failed, QP_TEST_RESULT_PASS otherwise.
*
* Note the function throws exception should an error occur!
*
* @return STOP if the test has finished, CONTINUE to indicate iterate() should be called once again.
**/
tcu::TestNode::IterateResult TessellationShaderTrianglesDegenerateTriangle::iterate(void)
{
/* Do not execute if required extensions are not supported. */
if (!m_is_tessellation_shader_supported)
{
throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED);
}
/* Initialize ES test objects */
initTest();
/* We only need to use one vertex per so go for it */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, 1);
GLU_EXPECT_NO_ERROR(gl.getError(), "glPatchParameteriEXT() failed for GL_PATCH_VERTICES_EXT pname");
/* Iterate through all tests configured */
for (_tests_const_iterator test_iterator = m_tests.begin(); test_iterator != m_tests.end(); test_iterator++)
{
const _test_descriptor& test = *test_iterator;
/* Run the iteration */
gl.useProgram(test.po_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() failed");
/* Draw the test geometry */
gl.beginTransformFeedback(GL_TRIANGLES);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback(GL_TRIANGLES) failed.");
gl.drawArrays(m_glExtTokens.PATCHES, 0 /* first */, 1 /* count */);
GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays() failed");
gl.endTransformFeedback();
GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback() failed");
/* Map the BO with result data into user space */
const float* triangle_vertex_data =
(const float*)gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, /* offset */
sizeof(float) * 3 /* vec3 */ * 3 /* points */, GL_MAP_READ_BIT);
GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange() call failed");
/* Make sure the triangle data is correct. Since we cannot rely on any specific order
* of the result vertices, raise corresponding flag for each of the expected vertices.
*/
const float epsilon = 1e-5f;
bool is_zero_zero_one_present = false;
bool is_zero_one_zero_present = false;
bool is_one_zero_zero_present = false;
for (unsigned int n_vertex = 0; n_vertex < 3 /* vertices */; ++n_vertex)
{
const float* triangle_ptr = triangle_vertex_data + 3 * n_vertex;
if (de::abs(triangle_ptr[0]) < epsilon && de::abs(triangle_ptr[1]) < epsilon &&
de::abs(triangle_ptr[2] - 1.0f) < epsilon)
{
if (!is_zero_zero_one_present)
{
is_zero_zero_one_present = true;
}
else
{
TCU_FAIL("(0, 0, 1) vertex outputted more than once");
}
}
else if (de::abs(triangle_ptr[0]) < epsilon && de::abs(triangle_ptr[1] - 1.0f) < epsilon &&
de::abs(triangle_ptr[2]) < epsilon)
{
if (!is_zero_one_zero_present)
{
is_zero_one_zero_present = true;
}
else
{
TCU_FAIL("(0, 1, 0) vertex outputted more than once");
}
}
else if (de::abs(triangle_ptr[0] - 1.0f) < epsilon && de::abs(triangle_ptr[1]) < epsilon &&
de::abs(triangle_ptr[2]) < epsilon)
{
if (!is_one_zero_zero_present)
{
is_one_zero_zero_present = true;
}
else
{
TCU_FAIL("(1, 0, 0) vertex outputted more than once");
}
}
else
{
m_testCtx.getLog() << tcu::TestLog::Message << "Unexpected vertex"
<< " (" << triangle_vertex_data[0] << ", " << triangle_vertex_data[1] << ", "
<< triangle_vertex_data[2] << ")"
<< " encountered for a degenerate triangle." << tcu::TestLog::EndMessage;
TCU_FAIL("Invalid vertex was generated by the tessellator");
}
} /* for (all vertices) */
/* Unmap the BO */
gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer() call failed");
}
/* All done */
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
return STOP;
}
/** Constructor
*
* @param context Test context
**/
TessellationShaderTrianglesIdenticalTriangles::TessellationShaderTrianglesIdenticalTriangles(
Context& context, const ExtParameters& extParams)
: TestCaseBase(context, extParams, "identical_triangles",
"Verifies that tessellation coordinates generated by the tessellator "
"running in triangles mode do not change if second inner or fourth "
"outer tessellation level is changed")
, m_vao_id(0)
, m_utils(DE_NULL)
{
/* Left blank on purpose */
}
/** Deinitializes ES objects created for the test. */
void TessellationShaderTrianglesIdenticalTriangles::deinit()
{
/* Call base class' deinit() */
TestCaseBase::deinit();
if (!m_is_tessellation_shader_supported)
{
return;
}
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Unbind vertex array object */
gl.bindVertexArray(0);
/* Deallocate test variables */
if (m_vao_id != 0)
{
gl.deleteVertexArrays(1, &m_vao_id);
m_vao_id = 0;
}
/* Deinitialize utils instance */
if (m_utils != DE_NULL)
{
delete m_utils;
m_utils = DE_NULL;
}
}
/** Initializes ES objects necessary to run the test. */
void TessellationShaderTrianglesIdenticalTriangles::initTest()
{
/* Skip if required extensions are not supported. */
if (!m_is_tessellation_shader_supported)
{
throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED);
}
/* Initialize Utils instance */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
m_utils = new TessellationShaderUtils(gl, this);
/* Initialize vertex array object */
gl.genVertexArrays(1, &m_vao_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not generate vertex array object");
gl.bindVertexArray(m_vao_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding vertex array object!");
/* Retrieve GL_MAX_TESS_GEN_LEVEL_EXT value */
glw::GLint gl_max_tess_gen_level_value = 0;
gl.getIntegerv(m_glExtTokens.MAX_TESS_GEN_LEVEL, &gl_max_tess_gen_level_value);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() call failed for GL_MAX_TESS_GEN_LEVEL_EXT pname");
/* Initialize all test runs */
_tessellation_level_set_filter filter =
(_tessellation_level_set_filter)((int)TESSELLATION_LEVEL_SET_FILTER_ALL_COMBINATIONS |
(int)TESSELLATION_LEVEL_SET_FILTER_EXCLUDE_NEGATIVE_BASE_VALUE);
_tessellation_levels_set levels_sets = TessellationShaderUtils::getTessellationLevelSetForPrimitiveMode(
TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES, gl_max_tess_gen_level_value, filter);
for (_tessellation_levels_set_const_iterator set_iterator = levels_sets.begin(); set_iterator != levels_sets.end();
set_iterator++)
{
_run run;
const _tessellation_levels& set = *set_iterator;
memcpy(run.base_inner, set.inner, sizeof(run.base_inner));
memcpy(run.base_outer, set.outer, sizeof(run.base_outer));
run.reference_inner[0] = run.base_inner[0];
run.reference_inner[1] = run.base_inner[1] * 0.25f;
run.reference_outer[0] = run.base_outer[0];
run.reference_outer[1] = run.base_outer[1];
run.reference_outer[2] = run.base_outer[2];
run.reference_outer[3] = run.base_outer[3] * 0.25f;
/* Retrieve vertex data for both passes */
glw::GLint n_base_vertices = 0;
glw::GLint n_reference_vertices = 0;
n_base_vertices = m_utils->getAmountOfVerticesGeneratedByTessellator(
TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES, run.base_inner, run.base_outer,
TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, false);
n_reference_vertices = m_utils->getAmountOfVerticesGeneratedByTessellator(
TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES, run.reference_inner, run.reference_outer,
TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, false); /* is_point_mode_enabled */
if (n_base_vertices == 0)
{
m_testCtx.getLog() << tcu::TestLog::Message << "No vertices were generated by tessellator for: "
"inner tess levels:"
"["
<< run.base_inner[0] << ", " << run.base_inner[1] << "]"
", outer tess levels:"
"["
<< run.base_outer[0] << ", " << run.base_outer[1] << ", " << run.base_outer[2] << ", "
<< run.base_outer[3] << "]"
", primitive mode: triangles, vertex spacing: equal."
<< tcu::TestLog::EndMessage;
TCU_FAIL("Zero vertices were generated by tessellator for base test pass");
}
if (n_reference_vertices == 0)
{
m_testCtx.getLog() << tcu::TestLog::Message << "No vertices were generated by tessellator for: "
"inner tess levels:"
"["
<< run.reference_inner[0] << ", " << run.reference_inner[1] << "]"
", outer tess levels:"
"["
<< run.reference_outer[0] << ", " << run.reference_outer[1] << ", "
<< run.reference_outer[2] << ", " << run.reference_outer[3]
<< "]"
", primitive mode: triangles, vertex spacing: equal."
<< tcu::TestLog::EndMessage;
TCU_FAIL("Zero vertices were generated by tessellator for reference test pass");
}
if (n_base_vertices != n_reference_vertices)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Amount of vertices generated by the tessellator differs"
" for the following inner/outer configs: "
"inner tess levels:"
"["
<< run.base_inner[0] << ", " << run.base_inner[1] << "]"
", outer tess levels:"
"["
<< run.base_outer[0] << ", " << run.base_outer[1] << ", " << run.base_outer[2] << ", "
<< run.base_outer[3] << "]"
" and inner tess levels:"
"["
<< run.reference_inner[0] << ", " << run.reference_inner[1] << "]"
", outer tess levels:"
"["
<< run.reference_outer[0] << ", " << run.reference_outer[1] << ", "
<< run.reference_outer[2] << ", " << run.reference_outer[3]
<< "]"
", primitive mode: triangles, vertex spacing: equal."
<< tcu::TestLog::EndMessage;
TCU_FAIL("Amount of vertices generated by tessellator differs between base and references passes");
}
run.n_vertices = n_base_vertices;
run.base_data = m_utils->getDataGeneratedByTessellator(
run.base_inner, false, /* is_point_mode_enabled */
TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES, TESSELLATION_SHADER_VERTEX_ORDERING_CCW,
TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, run.base_outer);
run.reference_data = m_utils->getDataGeneratedByTessellator(
run.reference_inner, false, /* is_point_mode_enabled */
TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES, TESSELLATION_SHADER_VERTEX_ORDERING_CCW,
TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, run.reference_outer);
/* Store the run data */
m_runs.push_back(run);
} /* for (all sets) */
}
/** Executes the test.
*
* Sets the test result to QP_TEST_RESULT_FAIL if the test failed, QP_TEST_RESULT_PASS otherwise.
*
* Note the function throws exception should an error occur!
*
* @return STOP if the test has finished, CONTINUE to indicate iterate() should be called once again.
**/
tcu::TestNode::IterateResult TessellationShaderTrianglesIdenticalTriangles::iterate(void)
{
/* Do not execute if required extensions are not supported. */
if (!m_is_tessellation_shader_supported)
{
throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED);
}
/* Initialize the test */
initTest();
/* Iterate through all runs */
for (_runs_const_iterator run_iterator = m_runs.begin(); run_iterator != m_runs.end(); run_iterator++)
{
const _run& run = *run_iterator;
/* Make sure the vertex data generated for two passes matches */
const glw::GLint n_triangles = run.n_vertices / 3 /* vertices per triangle */;
for (int n_triangle = 0; n_triangle < n_triangles; ++n_triangle)
{
const float* triangle_a = (const float*)(&run.base_data[0]) +
n_triangle * 3 /* vertices */
* 3; /* components */
const float* triangle_b = (const float*)(&run.reference_data[0]) +
n_triangle * 3 /* vertices */
* 3; /* components */
if (!TessellationShaderUtils::isTriangleDefined(triangle_a, triangle_b))
{
m_testCtx.getLog() << tcu::TestLog::Message
<< "The following triangle, generated in the first pass, was not "
"generated in the other. "
"First pass' configuration: inner tess levels:"
"["
<< run.base_inner[0] << ", " << run.base_inner[1] << "]"
", outer tess levels:"
"["
<< run.base_outer[0] << ", " << run.base_outer[1] << ", " << run.base_outer[2]
<< ", " << run.base_outer[3] << "]"
"; second pass' configuration: inner tess levels:"
"["
<< run.reference_inner[0] << ", " << run.reference_inner[1] << "]"
", outer tess levels:"
"["
<< run.reference_outer[0] << ", " << run.reference_outer[1] << ", "
<< run.reference_outer[2] << ", " << run.reference_outer[3]
<< "]"
", primitive mode: triangles, vertex spacing: equal."
<< tcu::TestLog::EndMessage;
TCU_FAIL("A triangle from base vertex data set was not found in reference vertex data set.");
}
} /* for (all vertices) */
} /* for (all runs) */
/* All done */
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
return STOP;
}
/** Constructor
*
* @param context Test context
**/
TessellationShaderTrianglesInnerTessellationLevelRounding::TessellationShaderTrianglesInnerTessellationLevelRounding(
Context& context, const ExtParameters& extParams)
: TestCaseBase(context, extParams, "inner_tessellation_level_rounding",
"Verifies that inner tessellation level is rounded to 1 or 2,"
" when the tessellator is run in triangles primitive mode and "
"inner tessellation level is set to 1 and any of the outer "
"tessellation levels is greater than one.")
, m_vao_id(0)
, m_utils(DE_NULL)
{
/* Left blank on purpose */
}
/** Deinitializes ES objects created for the test. */
void TessellationShaderTrianglesInnerTessellationLevelRounding::deinit()
{
/* Call base class' deinit() */
TestCaseBase::deinit();
if (!m_is_tessellation_shader_supported)
{
return;
}
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Unbind vertex array object */
gl.bindVertexArray(0);
/* Deallocate test variables */
if (m_vao_id != 0)
{
gl.deleteVertexArrays(1, &m_vao_id);
m_vao_id = 0;
}
/* Deinitialize utils instance */
if (m_utils != DE_NULL)
{
delete m_utils;
m_utils = DE_NULL;
}
}
/** Executes the test.
*
* Sets the test result to QP_TEST_RESULT_FAIL if the test failed, QP_TEST_RESULT_PASS otherwise.
*
* Note the function throws exception should an error occur!
*
* @return STOP if the test has finished, CONTINUE to indicate iterate() should be called once again.
**/
tcu::TestNode::IterateResult TessellationShaderTrianglesInnerTessellationLevelRounding::iterate(void)
{
/* Do not execute if required extensions are not supported. */
if (!m_is_tessellation_shader_supported)
{
throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED);
}
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Initialize vertex array object */
gl.genVertexArrays(1, &m_vao_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not generate vertex array object");
gl.bindVertexArray(m_vao_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding vertex array object!");
/* Initialize and run test iterations. In later part, we will verify the generated data. */
runTestIterations();
/* Iterate through all runs */
for (_runs_const_iterator run_iterator = m_runs.begin(); run_iterator != m_runs.end(); run_iterator++)
{
const _run& run = *run_iterator;
if (run.vertex_spacing == TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD &&
de::abs(run.set1_inner[0] - run.set2_inner[0]) > 1e-5f &&
de::min(run.set1_inner[0], run.set2_inner[0]) >= 1.0f)
{
/* In fractional_odd_spacing mode with inner level <f> >= 1.0f, the clamped
and rounded integer level <n> is at least 3.
These results in inner subdivision into at least <n>-2=1 segment and
two additional, typically shorter segments.
The length of these two additional segments relative to the others will
decrease monotonically with the value of <n>-<f>, so if different <f> levels
were used, we cannot proceed with matching the exact vertex data. */
continue;
}
/* Make sure the vertex data generated for two passes matches */
const glw::GLint n_triangles = run.n_vertices / 3 /* vertices per triangle */;
for (int n_triangle = 0; n_triangle < n_triangles; ++n_triangle)
{
const float* triangle_a = (const float*)(&run.set1_data[0]) +
n_triangle * 3 /* vertices */
* 3; /* components */
const float* triangle_b = (const float*)(&run.set2_data[0]) +
n_triangle * 3 /* vertices */
* 3; /* components */
if (!TessellationShaderUtils::isTriangleDefined(triangle_a, triangle_b))
{
std::string vs_mode_string =
TessellationShaderUtils::getESTokenForVertexSpacingMode(run.vertex_spacing);
m_testCtx.getLog() << tcu::TestLog::Message
<< "The following triangle, generated in the first pass, was not "
"generated in the second one. "
"First pass' configuration: inner tess levels:"
"["
<< run.set1_inner[0] << ", " << run.set1_inner[1] << "]"
", outer tess levels:"
"["
<< run.set1_outer[0] << ", " << run.set1_outer[1] << ", " << run.set1_outer[2]
<< ", " << run.set1_outer[3] << "]"
"; second pass' configuration: inner tess levels:"
"["
<< run.set2_inner[0] << ", " << run.set2_inner[1] << "]"
", outer tess levels:"
"["
<< run.set2_outer[0] << ", " << run.set2_outer[1] << ", " << run.set2_outer[2]
<< ", " << run.set2_outer[3] << "]"
", primitive mode: triangles, vertex spacing: "
<< vs_mode_string << tcu::TestLog::EndMessage;
TCU_FAIL("A triangle from first pass' data set was not found in second pass' data set.");
}
} /* for (all vertices) */
} /* for (all runs) */
/* All done */
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
return STOP;
}
/** Runs all test iterations needed to generate data for later verification. */
void TessellationShaderTrianglesInnerTessellationLevelRounding::runTestIterations()
{
/* Skip if required extensions are not supported. */
if (!m_is_tessellation_shader_supported)
{
throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED);
}
/* Initialize Utils instance */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
m_utils = new TessellationShaderUtils(gl, this);
/* Retrieve GL_MAX_TESS_GEN_LEVEL_EXT value */
glw::GLint gl_max_tess_gen_level_value = 0;
gl.getIntegerv(m_glExtTokens.MAX_TESS_GEN_LEVEL, &gl_max_tess_gen_level_value);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() call failed for GL_MAX_TESS_GEN_LEVEL_EXT pname");
/* Initialize all test runs */
const glw::GLint tess_levels[] = { 2, gl_max_tess_gen_level_value / 2, gl_max_tess_gen_level_value };
const unsigned int n_tess_levels = sizeof(tess_levels) / sizeof(tess_levels[0]);
const _tessellation_shader_vertex_spacing vs_modes[] = { TESSELLATION_SHADER_VERTEX_SPACING_EQUAL,
TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_EVEN,
TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD };
const unsigned int n_vs_modes = sizeof(vs_modes) / sizeof(vs_modes[0]);
for (unsigned int n_vs_mode = 0; n_vs_mode < n_vs_modes; ++n_vs_mode)
{
_tessellation_shader_vertex_spacing vs_mode = vs_modes[n_vs_mode];
for (unsigned int n_tess_level = 0; n_tess_level < n_tess_levels; ++n_tess_level)
{
/* Set up the run descriptor */
glw::GLint tess_level = tess_levels[n_tess_level];
_run run;
run.set1_inner[0] = 1.0f;
run.set1_inner[1] = 0.0f;
run.set2_inner[1] = 0.0f;
TessellationShaderUtils::getTessellationLevelAfterVertexSpacing(vs_mode, 1.5f, gl_max_tess_gen_level_value,
DE_NULL, /* out_clamped */
run.set2_inner);
run.set1_outer[0] = (glw::GLfloat)tess_level;
run.set2_outer[0] = (glw::GLfloat)tess_level;
run.set1_outer[1] = (glw::GLfloat)tess_level;
run.set2_outer[1] = (glw::GLfloat)tess_level;
run.set1_outer[2] = (glw::GLfloat)tess_level;
run.set2_outer[2] = (glw::GLfloat)tess_level;
run.set1_outer[3] = (glw::GLfloat)tess_level;
run.set2_outer[3] = (glw::GLfloat)tess_level;
run.vertex_spacing = vs_mode;
/* Retrieve vertex data for both passes */
glw::GLint n_set1_vertices = 0;
glw::GLint n_set2_vertices = 0;
n_set1_vertices = m_utils->getAmountOfVerticesGeneratedByTessellator(
TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES, run.set1_inner, run.set1_outer, run.vertex_spacing,
false); /* is_point_mode_enabled */
n_set2_vertices = m_utils->getAmountOfVerticesGeneratedByTessellator(
TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES, run.set2_inner, run.set2_outer, run.vertex_spacing,
false); /* is_point_mode_enabled */
if (n_set1_vertices == 0)
{
std::string vs_mode_string = TessellationShaderUtils::getESTokenForVertexSpacingMode(vs_mode);
m_testCtx.getLog() << tcu::TestLog::Message << "No vertices were generated by tessellator for: "
"inner tess levels:"
"["
<< run.set1_inner[0] << ", " << run.set1_inner[1] << "]"
", outer tess levels:"
"["
<< run.set1_outer[0] << ", " << run.set1_outer[1] << ", " << run.set1_outer[2]
<< ", " << run.set1_outer[3] << "]"
", primitive mode: triangles, "
"vertex spacing: "
<< vs_mode_string << tcu::TestLog::EndMessage;
TCU_FAIL("Zero vertices were generated by tessellator for first test pass");
}
if (n_set2_vertices == 0)
{
std::string vs_mode_string = TessellationShaderUtils::getESTokenForVertexSpacingMode(vs_mode);
m_testCtx.getLog() << tcu::TestLog::Message << "No vertices were generated by tessellator for: "
"inner tess levels:"
"["
<< run.set2_inner[0] << ", " << run.set2_inner[1] << "]"
", outer tess levels:"
"["
<< run.set2_outer[0] << ", " << run.set2_outer[1] << ", " << run.set2_outer[2]
<< ", " << run.set2_outer[3] << "]"
", primitive mode: triangles, "
"vertex spacing: "
<< vs_mode_string << tcu::TestLog::EndMessage;
TCU_FAIL("Zero vertices were generated by tessellator for second test pass");
}
if (n_set1_vertices != n_set2_vertices)
{
std::string vs_mode_string = TessellationShaderUtils::getESTokenForVertexSpacingMode(vs_mode);
m_testCtx.getLog() << tcu::TestLog::Message << "Amount of vertices generated by the tessellator differs"
" for the following inner/outer configs: "
"inner tess levels:"
"["
<< run.set1_inner[0] << ", " << run.set1_inner[1] << "]"
", outer tess levels:"
"["
<< run.set1_outer[0] << ", " << run.set1_outer[1] << ", " << run.set1_outer[2]
<< ", " << run.set1_outer[3] << "]"
" and inner tess levels:"
"["
<< run.set2_inner[0] << ", " << run.set2_inner[1] << "]"
", outer tess levels:"
"["
<< run.set2_outer[0] << ", " << run.set2_outer[1] << ", " << run.set2_outer[2]
<< ", " << run.set2_outer[3] << "]"
", primitive mode: triangles, vertex spacing: "
<< vs_mode_string << tcu::TestLog::EndMessage;
TCU_FAIL("Amount of vertices generated by tessellator differs between base and references passes");
}
run.n_vertices = n_set1_vertices;
run.set1_data = m_utils->getDataGeneratedByTessellator(run.set1_inner, false, /* is_point_mode_enabled */
TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES,
TESSELLATION_SHADER_VERTEX_ORDERING_CCW,
run.vertex_spacing, run.set1_outer);
run.set2_data = m_utils->getDataGeneratedByTessellator(run.set2_inner, false, /* is_point_mode_enabled */
TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES,
TESSELLATION_SHADER_VERTEX_ORDERING_CCW,
run.vertex_spacing, run.set2_outer);
/* Store the run data */
m_runs.push_back(run);
} /* for (all sets) */
} /* for (all vertex spacing modes) */
}
} /* namespace glcts */