| /*------------------------------------------------------------------------- |
| * 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 "esextcTessellationShaderInvariance.hpp" |
| #include "gluContextInfo.hpp" |
| #include "gluDefs.hpp" |
| #include "glwEnums.hpp" |
| #include "glwFunctions.hpp" |
| #include "tcuTestLog.hpp" |
| |
| namespace glcts |
| { |
| |
| /* Defines a single vertex in tessellation space */ |
| typedef struct _vertex |
| { |
| float u; |
| float v; |
| float w; |
| |
| _vertex() |
| { |
| u = 0.0f; |
| v = 0.0f; |
| w = 0.0f; |
| } |
| } _vertex; |
| |
| /** Constructor |
| * |
| * @param context Test context |
| **/ |
| TessellationShaderInvarianceTests::TessellationShaderInvarianceTests(glcts::Context& context, |
| const ExtParameters& extParams) |
| : TestCaseGroupBase(context, extParams, "tessellation_invariance", |
| "Verifies the implementation conforms to invariance rules.") |
| { |
| /* No implementation needed */ |
| } |
| |
| /** |
| * Initializes test groups for geometry shader tests |
| **/ |
| void TessellationShaderInvarianceTests::init(void) |
| { |
| addChild(new glcts::TessellationShaderInvarianceRule1Test(m_context, m_extParams)); |
| addChild(new glcts::TessellationShaderInvarianceRule2Test(m_context, m_extParams)); |
| addChild(new glcts::TessellationShaderInvarianceRule3Test(m_context, m_extParams)); |
| addChild(new glcts::TessellationShaderInvarianceRule4Test(m_context, m_extParams)); |
| addChild(new glcts::TessellationShaderInvarianceRule5Test(m_context, m_extParams)); |
| addChild(new glcts::TessellationShaderInvarianceRule6Test(m_context, m_extParams)); |
| addChild(new glcts::TessellationShaderInvarianceRule7Test(m_context, m_extParams)); |
| } |
| |
| /** Constructor |
| * |
| * @param context Test context |
| * @param name Test name |
| * @param description Test description |
| **/ |
| TessellationShaderInvarianceBaseTest::TessellationShaderInvarianceBaseTest(Context& context, |
| const ExtParameters& extParams, |
| const char* name, const char* description) |
| : TestCaseBase(context, extParams, name, description) |
| , m_utils_ptr(DE_NULL) |
| , m_bo_id(0) |
| , m_qo_tfpw_id(0) |
| , m_vao_id(0) |
| { |
| /* Left blank on purpose */ |
| } |
| |
| /** Deinitializes ES objects created for the test. */ |
| void TessellationShaderInvarianceBaseTest::deinit() |
| { |
| /* Call base class' deinit() */ |
| TestCaseBase::deinit(); |
| |
| if (!m_is_tessellation_shader_supported) |
| { |
| return; |
| } |
| |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| /* Revert buffer object bindings */ |
| gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* buffer */); |
| gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* index */, 0 /* buffer */); |
| |
| /* Disable GL_RASTERIZER_DISCARD mode */ |
| gl.disable(GL_RASTERIZER_DISCARD); |
| |
| /* Reset GL_PATCH_VERTICES_EXT to default value */ |
| gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, 3); |
| |
| /* Unbind vertex array object */ |
| gl.bindVertexArray(0); |
| |
| /* Deinitialize all ES objects that were created for test purposes */ |
| if (m_bo_id != 0) |
| { |
| gl.deleteBuffers(1, &m_bo_id); |
| |
| m_bo_id = 0; |
| } |
| |
| for (_programs_iterator it = m_programs.begin(); it != m_programs.end(); ++it) |
| { |
| _test_program& program = *it; |
| |
| if (program.po_id != 0) |
| { |
| gl.deleteProgram(program.po_id); |
| } |
| } |
| m_programs.clear(); |
| |
| if (m_qo_tfpw_id != 0) |
| { |
| gl.deleteQueries(1, &m_qo_tfpw_id); |
| |
| m_qo_tfpw_id = 0; |
| } |
| |
| if (m_vao_id != 0) |
| { |
| gl.deleteVertexArrays(1, &m_vao_id); |
| |
| m_vao_id = 0; |
| } |
| |
| /* Deinitialize TS utils instance */ |
| if (m_utils_ptr != NULL) |
| { |
| delete m_utils_ptr; |
| |
| m_utils_ptr = NULL; |
| } |
| } |
| |
| /** Executes a single-counted GL_PATCHES_EXT draw call. |
| * |
| * Throws TestError exception if an error occurs. |
| * |
| * @param n_iteration Not used. |
| * |
| **/ |
| void TessellationShaderInvarianceBaseTest::executeDrawCall(unsigned int n_iteration) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| DE_UNREF(n_iteration); |
| |
| gl.drawArrays(m_glExtTokens.PATCHES, 0 /* first */, getDrawCallCountArgument()); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays() call failed"); |
| } |
| |
| /** Returns a value that should be used for the draw call's "count" argument. |
| * |
| * @param Always 1 |
| **/ |
| unsigned int TessellationShaderInvarianceBaseTest::getDrawCallCountArgument() |
| { |
| return 1; |
| } |
| |
| /** Returns source code for fragment shader stage which does |
| * not do anything. |
| * |
| * @param n_iteration Not used. |
| * |
| * @return Requested string. |
| **/ |
| std::string TessellationShaderInvarianceBaseTest::getFSCode(unsigned int n_iteration) |
| { |
| DE_UNREF(n_iteration); |
| |
| std::string result = "${VERSION}\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| "}\n"; |
| |
| return result; |
| } |
| |
| /** Retrieves name of a vec2 uniform that stores inner tesselaton level information, |
| * later assigned to gl_TessLevelInner in tessellation evaluation shader. |
| * |
| * @return Requested name. |
| **/ |
| const char* TessellationShaderInvarianceBaseTest::getInnerTessLevelUniformName() |
| { |
| static const char* result = "inner_tess_level"; |
| |
| return result; |
| } |
| |
| /** Retrieves name of a vec4 uniform that stores outer tesselation level information, |
| * later assigned to gl_TessLevelOuter in tessellation evaluation shader. |
| * |
| * @return Requested name. |
| **/ |
| const char* TessellationShaderInvarianceBaseTest::getOuterTessLevelUniformName() |
| { |
| static const char* result = "outer_tess_level"; |
| |
| return result; |
| } |
| |
| /** Returns generic tessellation control shader code, which sends 4 output patch |
| * to tessellation evaluation shader stage and uses the very first input patch |
| * vertex only. |
| * |
| * @return Tessellation control source code. |
| */ |
| std::string TessellationShaderInvarianceBaseTest::getTCCode(unsigned int n_iteration) |
| { |
| DE_UNREF(n_iteration); |
| |
| /* In order to support all three primitive types, our generic tessellation |
| * control shader will pass 4 vertices to TE stage */ |
| return TessellationShaderUtils::getGenericTCCode(4, /* n_patch_vertices */ |
| false); |
| } |
| |
| /** Retrieves XFB properties for the test pass. |
| * |
| * @param n_iteration Not used. |
| * @param out_n_names Deref will be used to store amount of strings @param *out_n_names |
| * offers. |
| * @param out_names Deref will be used to store pointer to an array of strings holding |
| * names of varyings that should be captured via transform feedback. |
| * Must not be NULL. |
| * |
| **/ |
| void TessellationShaderInvarianceBaseTest::getXFBProperties(unsigned int n_iteration, unsigned int* out_n_names, |
| const char*** out_names) |
| { |
| static const char* names[] = { "result_uvw" }; |
| |
| DE_UNREF(n_iteration); |
| |
| *out_n_names = 1; |
| *out_names = names; |
| } |
| |
| /** Returns vertex shader source code. The shader sets gl_Position to |
| * vec4(1, 2, 3, 0). |
| * |
| * @return Vertex shader source code. |
| **/ |
| std::string TessellationShaderInvarianceBaseTest::getVSCode(unsigned int n_iteration) |
| { |
| DE_UNREF(n_iteration); |
| |
| std::string result = "${VERSION}\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = vec4(1.0, 2.0, 3.0, 0.0);\n" |
| "}\n"; |
| |
| return result; |
| } |
| |
| /** Initializes ES objects required to execute the test. |
| * |
| * Throws TestError exception if an error occurs. |
| * |
| **/ |
| void TessellationShaderInvarianceBaseTest::initTest() |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| glw::GLuint shared_fs_id = 0; |
| glw::GLuint shared_tc_id = 0; |
| glw::GLuint shared_te_id = 0; |
| glw::GLuint shared_vs_id = 0; |
| |
| 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 GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN query object */ |
| gl.genQueries(1, &m_qo_tfpw_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGenQueries() call failed"); |
| |
| /* Initialize tessellation shader utils */ |
| m_utils_ptr = new TessellationShaderUtils(gl, this); |
| |
| /* Initialize a buffer object we will use to store XFB data. |
| * Note: we intentionally skip a glBufferData() call here, |
| * the actual buffer storage size is iteration-specific. |
| **/ |
| gl.genBuffers(1, &m_bo_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers() call failed"); |
| |
| gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_bo_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call failed"); |
| |
| gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, /* index */ |
| m_bo_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase() call failed"); |
| |
| /* Iterate through all iterations */ |
| const unsigned int n_iterations = getAmountOfIterations(); |
| m_programs.reserve(n_iterations); |
| |
| const glw::GLenum SHADER_TYPE_FRAGMENT = GL_FRAGMENT_SHADER; |
| const glw::GLenum SHADER_TYPE_TESSELLATION_CONTROL = m_glExtTokens.TESS_CONTROL_SHADER; |
| const glw::GLenum SHADER_TYPE_TESSELLATION_EVALUATION = m_glExtTokens.TESS_EVALUATION_SHADER; |
| const glw::GLenum SHADER_TYPE_VERTEX = GL_VERTEX_SHADER; |
| |
| for (unsigned int n_iteration = 0; n_iteration < n_iterations; ++n_iteration) |
| { |
| _test_program program; |
| |
| /* Create an iteration-specific program object */ |
| program.po_id = gl.createProgram(); |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() call failed."); |
| |
| /* Query the implementation on which shader types should be compiled on |
| * a per-iteration basis, and which can be initialized only once */ |
| static const glw::GLenum shader_types[] = { SHADER_TYPE_FRAGMENT, SHADER_TYPE_TESSELLATION_CONTROL, |
| SHADER_TYPE_TESSELLATION_EVALUATION, SHADER_TYPE_VERTEX }; |
| static const unsigned int n_shader_types = sizeof(shader_types) / sizeof(shader_types[0]); |
| |
| for (unsigned int n_shader_type = 0; n_shader_type < n_shader_types; ++n_shader_type) |
| { |
| std::string shader_body; |
| const char* shader_body_ptr = DE_NULL; |
| glw::GLuint shader_id = 0; |
| glw::GLenum shader_type = shader_types[n_shader_type]; |
| glw::GLenum shader_type_es = (glw::GLenum)shader_type; |
| |
| // Check whether the test should use a separate program objects for each iteration. |
| bool is_shader_iteration_specific = false; |
| if (shader_type == SHADER_TYPE_TESSELLATION_EVALUATION) |
| { |
| is_shader_iteration_specific = true; |
| } |
| else if ((shader_type != SHADER_TYPE_FRAGMENT) && (shader_type != SHADER_TYPE_TESSELLATION_CONTROL) && |
| (shader_type != SHADER_TYPE_VERTEX)) |
| { |
| TCU_FAIL("Unrecognized shader type"); |
| } |
| |
| /* We need to initialize the shader object if: |
| * |
| * - its body differs between iterations; |
| * - its body is shared by all iterations AND this is the first iteration |
| */ |
| bool has_shader_been_generated = false; |
| |
| if ((!is_shader_iteration_specific && n_iteration == 0) || is_shader_iteration_specific) |
| { |
| /* Create the shader object */ |
| has_shader_been_generated = true; |
| shader_id = gl.createShader(shader_type_es); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() call failed"); |
| |
| /* Assign shader body to the object */ |
| if (shader_type == SHADER_TYPE_FRAGMENT) |
| { |
| shader_body = getFSCode(n_iteration); |
| } |
| else if (shader_type == SHADER_TYPE_TESSELLATION_CONTROL) |
| { |
| shader_body = getTCCode(n_iteration); |
| } |
| else if (shader_type == SHADER_TYPE_TESSELLATION_EVALUATION) |
| { |
| shader_body = getTECode(n_iteration); |
| } |
| else if (shader_type == SHADER_TYPE_VERTEX) |
| { |
| shader_body = getVSCode(n_iteration); |
| } |
| else |
| { |
| TCU_FAIL("Unrecognized shader type"); |
| } |
| |
| shader_body_ptr = shader_body.c_str(); |
| |
| shaderSourceSpecialized(shader_id, 1, &shader_body_ptr); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() call failed"); |
| |
| /* Compile the shader object */ |
| m_utils_ptr->compileShaders(1, /* n_shaders */ |
| &shader_id, true); /* should_succeed */ |
| |
| /* If this is a shader object that will be shared by all iterations, cache it |
| * in a dedicated variable */ |
| if (!is_shader_iteration_specific) |
| { |
| if (shader_type == SHADER_TYPE_FRAGMENT) |
| { |
| shared_fs_id = shader_id; |
| } |
| else if (shader_type == SHADER_TYPE_TESSELLATION_CONTROL) |
| { |
| shared_tc_id = shader_id; |
| } |
| else if (shader_type == SHADER_TYPE_TESSELLATION_EVALUATION) |
| { |
| shared_te_id = shader_id; |
| } |
| else if (shader_type == SHADER_TYPE_VERTEX) |
| { |
| shared_vs_id = shader_id; |
| } |
| else |
| { |
| TCU_FAIL("Unrecognized shader type"); |
| } |
| } /* if (!is_shader_iteration_specific) */ |
| } /* if (shader object needs to be initialized) */ |
| else |
| { |
| shader_id = (shader_type == SHADER_TYPE_FRAGMENT) ? |
| shared_fs_id : |
| (shader_type == SHADER_TYPE_TESSELLATION_CONTROL) ? |
| shared_tc_id : |
| (shader_type == SHADER_TYPE_TESSELLATION_EVALUATION) ? shared_te_id : shared_vs_id; |
| } |
| |
| /* Attach the shader object to iteration-specific program object */ |
| gl.attachShader(program.po_id, shader_id); |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader() call failed."); |
| |
| /* Now that the object has been attached, we can flag it for deletion */ |
| if (has_shader_been_generated) |
| { |
| gl.deleteShader(shader_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteShader() call failed"); |
| } |
| } /* for (all shader types) */ |
| |
| /* Set up transform feed-back */ |
| unsigned int n_xfb_names = 0; |
| const char** xfb_names = NULL; |
| |
| getXFBProperties(n_iteration, &n_xfb_names, &xfb_names); |
| |
| gl.transformFeedbackVaryings(program.po_id, n_xfb_names, xfb_names, GL_INTERLEAVED_ATTRIBS); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings() call failed."); |
| |
| /* Try to link the program object */ |
| glw::GLint link_status = GL_FALSE; |
| |
| gl.linkProgram(program.po_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glLinkProgram() call failed"); |
| |
| gl.getProgramiv(program.po_id, GL_LINK_STATUS, &link_status); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv() call failed"); |
| |
| if (link_status != GL_TRUE) |
| { |
| TCU_FAIL("Program linking failed"); |
| } |
| |
| /* Retrieve inner/outer tess level uniform locations */ |
| program.inner_tess_level_uniform_location = |
| gl.getUniformLocation(program.po_id, getInnerTessLevelUniformName()); |
| program.outer_tess_level_uniform_location = |
| gl.getUniformLocation(program.po_id, getOuterTessLevelUniformName()); |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGetUniformLocation() call(s) failed"); |
| |
| /* Store the program object */ |
| m_programs.push_back(program); |
| } /* for (all iterations) */ |
| } |
| |
| /** 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 TessellationShaderInvarianceBaseTest::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 all objects needed to run the test */ |
| initTest(); |
| |
| /* Do a general set-up */ |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| gl.enable(GL_RASTERIZER_DISCARD); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glEnable(GL_RASTERIZER_DISCARD) failed."); |
| |
| gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, 1); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glPatchParameteriEXT() failed for GL_PATCH_VERTICES_EXT pname"); |
| |
| /* There are two types of verification supported by this base test implementation: |
| * |
| * - iteration-specific (verifyResultDataForIteration() ) |
| * - global (verifyResultData() ) |
| * |
| * It is up to test implementation to decide which of the two (or perhaps both) |
| * entry-points it should overload and use for validating the result data. |
| */ |
| const unsigned int n_iterations = getAmountOfIterations(); |
| char** iteration_data = new char*[n_iterations]; |
| |
| /* Execute the test */ |
| for (unsigned int n_iteration = 0; n_iteration < n_iterations; ++n_iteration) |
| { |
| _test_program& program = m_programs[n_iteration]; |
| |
| /* Retrieve iteration properties for current iteration */ |
| unsigned int bo_size = 0; |
| float inner_tess_levels[2] = { 0 }; |
| bool is_point_mode = false; |
| float outer_tess_levels[4] = { 0 }; |
| _tessellation_primitive_mode primitive_mode = TESSELLATION_SHADER_PRIMITIVE_MODE_UNKNOWN; |
| _tessellation_shader_vertex_ordering vertex_ordering = TESSELLATION_SHADER_VERTEX_ORDERING_UNKNOWN; |
| |
| getIterationProperties(n_iteration, inner_tess_levels, outer_tess_levels, &is_point_mode, &primitive_mode, |
| &vertex_ordering, &bo_size); |
| |
| DE_ASSERT(bo_size != 0); |
| |
| /* Activate iteration-specific program */ |
| gl.useProgram(program.po_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() failed."); |
| |
| /* Set up buffer object storage */ |
| { |
| char* zero_bo_data = new char[bo_size]; |
| |
| memset(zero_bo_data, 0, bo_size); |
| |
| gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, bo_size, zero_bo_data, GL_STATIC_DRAW); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() failed"); |
| |
| delete[] zero_bo_data; |
| zero_bo_data = NULL; |
| } |
| |
| /* Allocate space for iteration-specific data */ |
| iteration_data[n_iteration] = new char[bo_size]; |
| |
| /* Configure inner/outer tessellation levels as requested for the iteration */ |
| gl.uniform2fv(program.inner_tess_level_uniform_location, 1, /* count */ |
| inner_tess_levels); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glUniform2fv() call failed"); |
| |
| gl.uniform4fv(program.outer_tess_level_uniform_location, 1, /* count */ |
| outer_tess_levels); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glUniform4fv() call failed"); |
| |
| /* Launch the TFPW query */ |
| gl.beginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, m_qo_tfpw_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), |
| "glBeginQuery() for GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN target failed."); |
| |
| /* Prepare for TF */ |
| glw::GLenum tf_mode = TessellationShaderUtils::getTFModeForPrimitiveMode(primitive_mode, is_point_mode); |
| |
| gl.beginTransformFeedback(tf_mode); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback() call failed"); |
| { |
| /* Execute the draw call */ |
| executeDrawCall(n_iteration); |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "Draw call failed"); |
| } |
| gl.endTransformFeedback(); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback() call failed"); |
| |
| gl.endQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN) call failed"); |
| |
| /* Make sure that we had sufficient amount of space in a buffer object we used to |
| * capture XFB data. |
| **/ |
| glw::GLuint n_tf_primitives_written = 0; |
| glw::GLuint used_tf_bo_size = 0; |
| |
| gl.getQueryObjectuiv(m_qo_tfpw_id, GL_QUERY_RESULT, &n_tf_primitives_written); |
| GLU_EXPECT_NO_ERROR(gl.getError(), |
| "Could not retrieve GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN query object result"); |
| |
| if (is_point_mode) |
| { |
| used_tf_bo_size = static_cast<glw::GLuint>(n_tf_primitives_written * sizeof(float) * 3 /* components */); |
| } |
| else if (primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES) |
| { |
| used_tf_bo_size = static_cast<glw::GLuint>(n_tf_primitives_written * sizeof(float) * 2 /* vertices */ * |
| 3 /* components */); |
| } |
| else |
| { |
| used_tf_bo_size = static_cast<glw::GLuint>(n_tf_primitives_written * sizeof(float) * 3 /* vertices */ * |
| 3 /* components */); |
| } |
| |
| if (used_tf_bo_size != bo_size) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Expected " << bo_size |
| << " to be filled with tessellation data, " |
| "only " |
| << used_tf_bo_size << "was used." << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL("Amount of primitives generated during TF does not match amount of primitives that were expected" |
| " to be generated by the tessellator"); |
| } |
| |
| /* Map the buffer object we earlier bound to GL_TRANSFORM_FEEDBACK_BUFFER |
| * target into process space. */ |
| const void* xfb_data = gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, /* offset */ |
| bo_size, GL_MAP_READ_BIT); |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange() call failed"); |
| |
| memcpy(iteration_data[n_iteration], xfb_data, bo_size); |
| |
| /* Unmap the buffer object */ |
| gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer() call failed"); |
| |
| /* Ask the test implementation to verify the results */ |
| verifyResultDataForIteration(n_iteration, iteration_data[n_iteration]); |
| } /* for (all iterations) */ |
| |
| /* Now that we've executed all iterations, we can call a global verification |
| * entry-point */ |
| verifyResultData((const void**)iteration_data); |
| |
| /* At this point we're safe to release space allocated for data coming from |
| * all the iterations */ |
| for (unsigned int n_iteration = 0; n_iteration < n_iterations; ++n_iteration) |
| { |
| char* iter_data = (char*)iteration_data[n_iteration]; |
| delete[] iter_data; |
| |
| iteration_data[n_iteration] = DE_NULL; |
| } /* for (all iterations) */ |
| |
| delete[] iteration_data; |
| iteration_data = DE_NULL; |
| |
| /* All done */ |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| return STOP; |
| } |
| |
| /* Does nothing (stub implementation) |
| * |
| * @param n_iteration Not used. |
| * @param data Not used. |
| * |
| **/ |
| void TessellationShaderInvarianceBaseTest::verifyResultDataForIteration(unsigned int n_iteration, const void* data) |
| { |
| DE_UNREF(n_iteration && data); |
| |
| /* Do nothing - this is just a stub. */ |
| } |
| |
| /* Does nothing (stub implementation) |
| * |
| * @param all_iterations_data Not used. |
| * |
| **/ |
| void TessellationShaderInvarianceBaseTest::verifyResultData(const void** all_iterations_data) |
| { |
| DE_UNREF(all_iterations_data); |
| |
| /* Do nothing - this is just a stub. */ |
| } |
| |
| /** Constructor. |
| * |
| * @param context Rendering context. |
| * |
| **/ |
| TessellationShaderInvarianceRule1Test::TessellationShaderInvarianceRule1Test(Context& context, |
| const ExtParameters& extParams) |
| : TessellationShaderInvarianceBaseTest(context, extParams, "invariance_rule1", |
| "Verifies conformance with first invariance rule") |
| { |
| /* Left blank intentionally */ |
| } |
| |
| /** Destructor. */ |
| TessellationShaderInvarianceRule1Test::~TessellationShaderInvarianceRule1Test() |
| { |
| /* Left blank intentionally */ |
| } |
| |
| /** Retrieves amount of iterations the base test implementation should run before |
| * calling global verification routine. |
| * |
| * @return Always 6. |
| **/ |
| unsigned int TessellationShaderInvarianceRule1Test::getAmountOfIterations() |
| { |
| return 6; |
| } |
| |
| /** Returns a value that should be used for the draw call's "count" argument. |
| * |
| * @param Always 3 |
| **/ |
| unsigned int TessellationShaderInvarianceRule1Test::getDrawCallCountArgument() |
| { |
| return 3; |
| } |
| |
| /** Retrieves iteration-specific tessellation properties. |
| * |
| * @param n_iteration Iteration index to retrieve the properties for. |
| * @param out_inner_tess_levels Deref will be used to store iteration-specific inner |
| * tessellation level values. Must not be NULL. |
| * @param out_outer_tess_levels Deref will be used to store iteration-specific outer |
| * tessellation level values. Must not be NULL. |
| * @param out_point_mode Deref will be used to store iteration-specific flag |
| * telling whether point mode should be enabled for given pass. |
| * Must not be NULL. |
| * @param out_primitive_mode Deref will be used to store iteration-specific primitive |
| * mode. Must not be NULL. |
| * @param out_vertex_ordering Deref will be used to store iteration-specific vertex ordering. |
| * Must not be NULL. |
| * @param out_result_buffer_size Deref will be used to store amount of bytes XFB buffer object |
| * storage should offer for the draw call to succeed. Must not |
| * be NULL. |
| **/ |
| void TessellationShaderInvarianceRule1Test::getIterationProperties( |
| unsigned int n_iteration, float* out_inner_tess_levels, float* out_outer_tess_levels, bool* out_point_mode, |
| _tessellation_primitive_mode* out_primitive_mode, _tessellation_shader_vertex_ordering* out_vertex_ordering, |
| unsigned int* out_result_buffer_size) |
| { |
| *out_vertex_ordering = TESSELLATION_SHADER_VERTEX_ORDERING_CCW; |
| |
| switch (n_iteration) |
| { |
| case 0: |
| case 5: |
| { |
| /* Triangles (point mode) */ |
| out_inner_tess_levels[0] = 1.0f; |
| out_outer_tess_levels[0] = 1.0f; |
| out_outer_tess_levels[1] = 1.0f; |
| out_outer_tess_levels[2] = 1.0f; |
| |
| *out_point_mode = true; |
| *out_primitive_mode = TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES; |
| |
| break; |
| } |
| |
| case 1: |
| case 3: |
| { |
| /* Lines */ |
| out_outer_tess_levels[0] = 1.0f; |
| out_outer_tess_levels[1] = 1.0f; |
| |
| *out_point_mode = false; |
| *out_primitive_mode = TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES; |
| |
| break; |
| } |
| |
| case 2: |
| case 4: |
| { |
| /* Triangles */ |
| out_inner_tess_levels[0] = 1.0f; |
| out_outer_tess_levels[0] = 1.0f; |
| out_outer_tess_levels[1] = 1.0f; |
| out_outer_tess_levels[2] = 1.0f; |
| |
| *out_point_mode = false; |
| *out_primitive_mode = TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES; |
| |
| break; |
| } |
| |
| default: |
| { |
| TCU_FAIL("Unrecognzied iteration index"); |
| } |
| } |
| |
| *out_result_buffer_size = m_utils_ptr->getAmountOfVerticesGeneratedByTessellator( |
| *out_primitive_mode, out_inner_tess_levels, out_outer_tess_levels, TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, |
| *out_point_mode); |
| |
| *out_result_buffer_size = static_cast<unsigned int>(*out_result_buffer_size * getDrawCallCountArgument() * |
| 3 /* components */ * sizeof(float)); |
| |
| DE_ASSERT(*out_result_buffer_size != 0); |
| } |
| |
| /** Retrieves iteration-specific tessellation evaluation shader code. |
| * |
| * @param n_iteration Iteration index, for which the source code is being obtained. |
| * |
| * @return Requested source code. |
| **/ |
| std::string TessellationShaderInvarianceRule1Test::getTECode(unsigned int n_iteration) |
| { |
| unsigned int bo_size = 0; |
| float inner_tess_levels[2] = { 0 }; |
| float outer_tess_levels[4] = { 0 }; |
| bool point_mode = false; |
| _tessellation_primitive_mode primitive_mode = TESSELLATION_SHADER_PRIMITIVE_MODE_UNKNOWN; |
| _tessellation_shader_vertex_ordering vertex_ordering = TESSELLATION_SHADER_VERTEX_ORDERING_UNKNOWN; |
| |
| getIterationProperties(n_iteration, inner_tess_levels, outer_tess_levels, &point_mode, &primitive_mode, |
| &vertex_ordering, &bo_size); |
| |
| return TessellationShaderUtils::getGenericTECode(TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, primitive_mode, |
| vertex_ordering, point_mode); |
| } |
| |
| /** Verifies result data. Accesses data generated by all iterations. |
| * |
| * Throws TestError exception if an error occurs. |
| * |
| * @param all_iterations_data An array of pointers to buffers, holding gl_TessCoord |
| * data generated by subsequent iterations. |
| **/ |
| void TessellationShaderInvarianceRule1Test::verifyResultData(const void** all_iterations_data) |
| { |
| const float* lines_vertex_data_1 = (const float*)all_iterations_data[1]; |
| const float* lines_vertex_data_2 = (const float*)all_iterations_data[3]; |
| const float* point_vertex_data_1 = (const float*)all_iterations_data[0]; |
| const float* point_vertex_data_2 = (const float*)all_iterations_data[5]; |
| const float* tris_vertex_data_1 = (const float*)all_iterations_data[2]; |
| const float* tris_vertex_data_2 = (const float*)all_iterations_data[4]; |
| |
| const unsigned int n_line_vertices = 2 /* vertices per line segment */ * getDrawCallCountArgument(); /* lines */ |
| const unsigned int n_point_vertices = 1 /* vertices per point */ * getDrawCallCountArgument(); /* points */ |
| const unsigned int n_tri_vertices = 3 /* vertices per triangle */ * getDrawCallCountArgument(); /* triangles */ |
| const unsigned int vertex_size = sizeof(float) * 3; /* components */ |
| |
| /* Make sure the data sets match, given different draw call ordering */ |
| for (int n_type = 0; n_type < 3 /* lines, points, tris */; ++n_type) |
| { |
| const float* data1_ptr = DE_NULL; |
| const float* data2_ptr = DE_NULL; |
| std::string data_type_string; |
| unsigned int n_vertices = 0; |
| |
| switch (n_type) |
| { |
| case 0: |
| { |
| data1_ptr = lines_vertex_data_1; |
| data2_ptr = lines_vertex_data_2; |
| data_type_string = "Line"; |
| n_vertices = n_line_vertices; |
| |
| break; |
| } |
| |
| case 1: |
| { |
| data1_ptr = point_vertex_data_1; |
| data2_ptr = point_vertex_data_2; |
| data_type_string = "Point"; |
| n_vertices = n_point_vertices; |
| |
| break; |
| } |
| |
| case 2: |
| { |
| data1_ptr = tris_vertex_data_1; |
| data2_ptr = tris_vertex_data_2; |
| data_type_string = "Triangle"; |
| n_vertices = n_tri_vertices; |
| |
| break; |
| } |
| |
| default: |
| { |
| TCU_FAIL("Internal error: type index was not recognized"); |
| } |
| } /* switch (n_type) */ |
| |
| /* Make sure the buffer storage in both cases has been modified */ |
| { |
| unsigned int zero_bo_size = vertex_size * n_vertices; |
| char* zero_bo_data = new char[vertex_size * n_vertices]; |
| |
| memset(zero_bo_data, 0, zero_bo_size); |
| |
| if (memcmp(data1_ptr, zero_bo_data, zero_bo_size) == 0 || |
| memcmp(data2_ptr, zero_bo_data, zero_bo_size) == 0) |
| { |
| TCU_FAIL("One of the draw calls has not outputted any data to XFB buffer object storage"); |
| } |
| |
| delete[] zero_bo_data; |
| zero_bo_data = NULL; |
| } |
| |
| /* Compare the data */ |
| if (memcmp(data1_ptr, data2_ptr, vertex_size * n_vertices) != 0) |
| { |
| std::stringstream logMessage; |
| |
| logMessage << data_type_string << " data rendered in pass 1: ("; |
| |
| for (unsigned int n_vertex = 0; n_vertex < n_vertices; ++n_vertex) |
| { |
| logMessage << data1_ptr[n_vertex]; |
| |
| if (n_vertex != (n_vertices - 1)) |
| { |
| logMessage << ", "; |
| } |
| else |
| { |
| logMessage << ") "; |
| } |
| } /* for (all vertices) */ |
| |
| logMessage << "and in pass 2: ("; |
| |
| for (unsigned int n_vertex = 0; n_vertex < n_vertices; ++n_vertex) |
| { |
| logMessage << data2_ptr[n_vertex]; |
| |
| if (n_vertex != (n_vertices - 1)) |
| { |
| logMessage << ", "; |
| } |
| else |
| { |
| logMessage << ") "; |
| } |
| } /* for (all vertices) */ |
| |
| logMessage << "do not match"; |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << logMessage.str().c_str() << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL("Data mismatch"); |
| } /* if (data mismatch) */ |
| } /* for (all primitive types) */ |
| } |
| |
| /** Constructor. |
| * |
| * @param context Rendering context. |
| * |
| **/ |
| TessellationShaderInvarianceRule2Test::TessellationShaderInvarianceRule2Test(Context& context, |
| const ExtParameters& extParams) |
| : TessellationShaderInvarianceBaseTest(context, extParams, "invariance_rule2", |
| "Verifies conformance with second invariance rule") |
| { |
| memset(m_n_tessellated_vertices, 0, sizeof(m_n_tessellated_vertices)); |
| } |
| |
| /** Destructor. */ |
| TessellationShaderInvarianceRule2Test::~TessellationShaderInvarianceRule2Test() |
| { |
| /* Left blank intentionally */ |
| } |
| |
| /** Retrieves amount of iterations the base test implementation should run before |
| * calling global verification routine. |
| * |
| * @return Always 4. |
| **/ |
| unsigned int TessellationShaderInvarianceRule2Test::getAmountOfIterations() |
| { |
| return 4; |
| } |
| |
| /** Retrieves iteration-specific tessellation properties. |
| * |
| * @param n_iteration Iteration index to retrieve the properties for. |
| * @param out_inner_tess_levels Deref will be used to store iteration-specific inner |
| * tessellation level values. Must not be NULL. |
| * @param out_outer_tess_levels Deref will be used to store iteration-specific outer |
| * tessellation level values. Must not be NULL. |
| * @param out_point_mode Deref will be used to store iteration-specific flag |
| * telling whether point mode should be enabled for given pass. |
| * Must not be NULL. |
| * @param out_primitive_mode Deref will be used to store iteration-specific primitive |
| * mode. Must not be NULL. |
| * @param out_vertex_ordering Deref will be used to store iteration-specific vertex |
| * ordering. Must not be NULL. |
| * @param out_result_buffer_size Deref will be used to store amount of bytes XFB buffer object |
| * storage should offer for the draw call to succeed. Can |
| * be NULL. |
| **/ |
| void TessellationShaderInvarianceRule2Test::getIterationProperties( |
| unsigned int n_iteration, float* out_inner_tess_levels, float* out_outer_tess_levels, bool* out_point_mode, |
| _tessellation_primitive_mode* out_primitive_mode, _tessellation_shader_vertex_ordering* out_vertex_ordering, |
| unsigned int* out_result_buffer_size) |
| { |
| *out_vertex_ordering = TESSELLATION_SHADER_VERTEX_ORDERING_CCW; |
| |
| switch (n_iteration) |
| { |
| case 0: |
| case 1: |
| { |
| /* Triangles */ |
| out_outer_tess_levels[0] = 2.0f; |
| out_outer_tess_levels[1] = 3.0f; |
| out_outer_tess_levels[2] = 4.0f; |
| |
| if (n_iteration == 0) |
| { |
| out_inner_tess_levels[0] = 4.0f; |
| out_inner_tess_levels[1] = 5.0f; |
| } |
| else |
| { |
| out_inner_tess_levels[0] = 3.0f; |
| out_inner_tess_levels[1] = 4.0f; |
| } |
| |
| *out_point_mode = false; |
| *out_primitive_mode = TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES; |
| |
| break; |
| } |
| |
| case 2: |
| case 3: |
| { |
| /* Quads */ |
| out_outer_tess_levels[0] = 2.0f; |
| out_outer_tess_levels[1] = 3.0f; |
| out_outer_tess_levels[2] = 4.0f; |
| out_outer_tess_levels[3] = 5.0f; |
| |
| if (n_iteration == 2) |
| { |
| out_inner_tess_levels[0] = 2.0f; |
| out_inner_tess_levels[1] = 3.0f; |
| } |
| else |
| { |
| out_inner_tess_levels[0] = 4.0f; |
| out_inner_tess_levels[1] = 5.0f; |
| } |
| |
| *out_point_mode = false; |
| *out_primitive_mode = TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS; |
| |
| break; |
| } |
| |
| default: |
| { |
| TCU_FAIL("Unrecognized iteration index"); |
| } |
| } |
| |
| if (out_result_buffer_size != DE_NULL) |
| { |
| *out_result_buffer_size = m_utils_ptr->getAmountOfVerticesGeneratedByTessellator( |
| *out_primitive_mode, out_inner_tess_levels, out_outer_tess_levels, TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, |
| *out_point_mode); |
| |
| m_n_tessellated_vertices[n_iteration] = *out_result_buffer_size; |
| *out_result_buffer_size = |
| static_cast<unsigned int>(*out_result_buffer_size * 3 /* components */ * sizeof(float)); |
| |
| DE_ASSERT(*out_result_buffer_size != 0); |
| } |
| } |
| |
| /** Retrieves iteration-specific tessellation evaluation shader code. |
| * |
| * @param n_iteration Iteration index, for which the source code is being obtained. |
| * |
| * @return Requested source code. |
| **/ |
| std::string TessellationShaderInvarianceRule2Test::getTECode(unsigned int n_iteration) |
| { |
| unsigned int bo_size = 0; |
| float inner_tess_levels[2] = { 0 }; |
| float outer_tess_levels[4] = { 0 }; |
| bool point_mode = false; |
| _tessellation_primitive_mode primitive_mode = TESSELLATION_SHADER_PRIMITIVE_MODE_UNKNOWN; |
| _tessellation_shader_vertex_ordering vertex_ordering = TESSELLATION_SHADER_VERTEX_ORDERING_UNKNOWN; |
| |
| getIterationProperties(n_iteration, inner_tess_levels, outer_tess_levels, &point_mode, &primitive_mode, |
| &vertex_ordering, &bo_size); |
| |
| return TessellationShaderUtils::getGenericTECode(TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, primitive_mode, |
| vertex_ordering, point_mode); |
| } |
| |
| /** Verifies result data. Accesses data generated by all iterations. |
| * |
| * Throws TestError exception if an error occurs. |
| * |
| * @param all_iterations_data An array of pointers to buffers, holding gl_TessCoord |
| * data generated by subsequent iterations. |
| **/ |
| void TessellationShaderInvarianceRule2Test::verifyResultData(const void** all_iterations_data) |
| { |
| /* Iterate through one tessellated set of vertices for a given primitive type |
| * and identify outer vertices. Make sure exactly the same vertices can be |
| * found in the other set of vertices, generated with different input tessellation |
| * level. |
| */ |
| for (int n_primitive_type = 0; n_primitive_type < 2; /* triangles / quads */ |
| ++n_primitive_type) |
| { |
| const float* data1_ptr = (const float*)all_iterations_data[n_primitive_type * 2 + 0]; |
| const float* data2_ptr = (const float*)all_iterations_data[n_primitive_type * 2 + 1]; |
| _tessellation_primitive_mode primitive_mode = (n_primitive_type == 0) ? |
| TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES : |
| TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS; |
| std::vector<_vertex> outer_vertices; |
| |
| /* Iterate through all data1 vertices.. */ |
| for (unsigned int n_vertex = 0; n_vertex < m_n_tessellated_vertices[n_primitive_type * 2]; ++n_vertex) |
| { |
| /* Check if any of the components is equal to 0 or 1. If so, this could |
| * be an edge vertex. |
| */ |
| const float* vertex_ptr = data1_ptr + n_vertex * 3; /* components */ |
| |
| if (TessellationShaderUtils::isOuterEdgeVertex(primitive_mode, vertex_ptr)) |
| { |
| /* Only add the vertex if it has not been added already to the vector */ |
| bool has_already_been_added = false; |
| |
| for (std::vector<_vertex>::const_iterator vertex_iterator = outer_vertices.begin(); |
| vertex_iterator != outer_vertices.end(); vertex_iterator++) |
| { |
| if (vertex_iterator->u == vertex_ptr[0] && vertex_iterator->v == vertex_ptr[1] && |
| vertex_iterator->w == vertex_ptr[2]) |
| { |
| has_already_been_added = true; |
| |
| break; |
| } |
| } /* for (all outer vertices stored so far) */ |
| |
| if (!has_already_been_added) |
| { |
| _vertex vertex; |
| |
| vertex.u = vertex_ptr[0]; |
| vertex.v = vertex_ptr[1]; |
| vertex.w = vertex_ptr[2]; |
| |
| outer_vertices.push_back(vertex); |
| } |
| } /* if (input vertex is located on outer edge) */ |
| } /* for (all input vertices) */ |
| |
| DE_ASSERT(outer_vertices.size() != 0); |
| |
| /* Now that we know where outer vertices are, make sure they are present in the other data set */ |
| for (std::vector<_vertex>::const_iterator ref_vertex_iterator = outer_vertices.begin(); |
| ref_vertex_iterator != outer_vertices.end(); ref_vertex_iterator++) |
| { |
| bool has_been_found = false; |
| const _vertex& ref_vertex = *ref_vertex_iterator; |
| |
| for (unsigned int n_vertex = 0; n_vertex < m_n_tessellated_vertices[n_primitive_type * 2 + 1]; ++n_vertex) |
| { |
| const float* vertex_ptr = data2_ptr + n_vertex * 3; /* components */ |
| |
| if (vertex_ptr[0] == ref_vertex.u && vertex_ptr[1] == ref_vertex.v && vertex_ptr[2] == ref_vertex.w) |
| { |
| has_been_found = true; |
| |
| break; |
| } |
| } /* for (all vertices in the other data set) */ |
| |
| if (!has_been_found) |
| { |
| float cmp_inner_tess_levels[2]; |
| float cmp_outer_tess_levels[4]; |
| bool cmp_point_mode; |
| _tessellation_primitive_mode cmp_primitive_mode; |
| _tessellation_shader_vertex_ordering cmp_vertex_ordering; |
| std::string primitive_type = (n_primitive_type == 0) ? "triangles" : "quads"; |
| float ref_inner_tess_levels[2]; |
| float ref_outer_tess_levels[4]; |
| bool ref_point_mode; |
| _tessellation_primitive_mode ref_primitive_mode; |
| _tessellation_shader_vertex_ordering ref_vertex_ordering; |
| |
| getIterationProperties(n_primitive_type * 2, ref_inner_tess_levels, ref_outer_tess_levels, |
| &ref_point_mode, &ref_primitive_mode, &ref_vertex_ordering, NULL); |
| getIterationProperties(n_primitive_type * 2 + 1, cmp_inner_tess_levels, cmp_outer_tess_levels, |
| &cmp_point_mode, &cmp_primitive_mode, &cmp_vertex_ordering, NULL); |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << "Outer vertex" |
| << " (" << ref_vertex.u << ", " << ref_vertex.v << ", " << ref_vertex.w << ")" |
| << " was not found in tessellated data coordinate set generated" |
| << " for primitive type: " << primitive_type.c_str() |
| << ". Reference inner tessellation level:" |
| << " (" << ref_inner_tess_levels[0] << ", " << ref_inner_tess_levels[1] |
| << "), reference outer tessellation level:" |
| << " (" << ref_outer_tess_levels[0] << ", " << ref_outer_tess_levels[1] << ", " |
| << ref_outer_tess_levels[2] << ", " << ref_outer_tess_levels[3] |
| << "), test inner tessellation level:" |
| << " (" << cmp_inner_tess_levels[0] << ", " << cmp_inner_tess_levels[1] |
| << "), reference outer tessellation level:" |
| << " (" << cmp_outer_tess_levels[0] << ", " << cmp_outer_tess_levels[1] << ", " |
| << cmp_outer_tess_levels[2] << ", " << cmp_outer_tess_levels[3] << ")." |
| << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL("Outer edge vertex was not found in tessellated coordinate set generated for " |
| "the same outer tessellation levels and vertex spacing, but different inner " |
| "tessellation levels"); |
| } /* if (outer edge vertex was not found in the other set) */ |
| } /* for (all outer edge vertices) */ |
| } /* for (both primitive types) */ |
| } |
| |
| /** Constructor. |
| * |
| * @param context Rendering context. |
| * |
| **/ |
| TessellationShaderInvarianceRule3Test::TessellationShaderInvarianceRule3Test(Context& context, |
| const ExtParameters& extParams) |
| : TessellationShaderInvarianceBaseTest(context, extParams, "invariance_rule3", |
| "Verifies conformance with third invariance rule") |
| { |
| } |
| |
| /** Destructor. */ |
| TessellationShaderInvarianceRule3Test::~TessellationShaderInvarianceRule3Test() |
| { |
| /* Left blank intentionally */ |
| } |
| |
| /** Retrieves amount of iterations the base test implementation should run before |
| * calling global verification routine. |
| * |
| * @return A value that depends on initTestIterations() behavior. |
| **/ |
| unsigned int TessellationShaderInvarianceRule3Test::getAmountOfIterations() |
| { |
| if (m_test_iterations.size() == 0) |
| { |
| initTestIterations(); |
| } |
| |
| return (unsigned int)m_test_iterations.size(); |
| } |
| |
| /** Retrieves iteration-specific tessellation properties. |
| * |
| * @param n_iteration Iteration index to retrieve the properties for. |
| * @param out_inner_tess_levels Deref will be used to store iteration-specific inner |
| * tessellation level values. Must not be NULL. |
| * @param out_outer_tess_levels Deref will be used to store iteration-specific outer |
| * tessellation level values. Must not be NULL. |
| * @param out_point_mode Deref will be used to store iteration-specific flag |
| * telling whether point mode should be enabled for given pass. |
| * Must not be NULL. |
| * @param out_primitive_mode Deref will be used to store iteration-specific primitive |
| * mode. Must not be NULL. |
| * @param out_vertex_ordering Deref will be used to store iteration-specific vertex |
| * ordering. Must not be NULL. |
| * @param out_result_buffer_size Deref will be used to store amount of bytes XFB buffer object |
| * storage should offer for the draw call to succeed. Can |
| * be NULL. |
| **/ |
| void TessellationShaderInvarianceRule3Test::getIterationProperties( |
| unsigned int n_iteration, float* out_inner_tess_levels, float* out_outer_tess_levels, bool* out_point_mode, |
| _tessellation_primitive_mode* out_primitive_mode, _tessellation_shader_vertex_ordering* out_vertex_ordering, |
| unsigned int* out_result_buffer_size) |
| { |
| DE_ASSERT(m_test_iterations.size() > n_iteration); |
| |
| _test_iteration& test_iteration = m_test_iterations[n_iteration]; |
| |
| memcpy(out_inner_tess_levels, test_iteration.inner_tess_levels, sizeof(test_iteration.inner_tess_levels)); |
| memcpy(out_outer_tess_levels, test_iteration.outer_tess_levels, sizeof(test_iteration.outer_tess_levels)); |
| |
| *out_point_mode = false; |
| *out_primitive_mode = test_iteration.primitive_mode; |
| *out_vertex_ordering = TESSELLATION_SHADER_VERTEX_ORDERING_CCW; |
| |
| if (out_result_buffer_size != DE_NULL) |
| { |
| *out_result_buffer_size = m_utils_ptr->getAmountOfVerticesGeneratedByTessellator( |
| *out_primitive_mode, out_inner_tess_levels, out_outer_tess_levels, test_iteration.vertex_spacing, |
| *out_point_mode); |
| test_iteration.n_vertices = *out_result_buffer_size; |
| *out_result_buffer_size = |
| static_cast<unsigned int>(*out_result_buffer_size * 3 /* components */ * sizeof(float)); |
| |
| DE_ASSERT(*out_result_buffer_size != 0); |
| } |
| } |
| |
| /** Retrieves iteration-specific tessellation evaluation shader code. |
| * |
| * @param n_iteration Iteration index, for which the source code is being obtained. |
| * |
| * @return Requested source code. |
| **/ |
| std::string TessellationShaderInvarianceRule3Test::getTECode(unsigned int n_iteration) |
| { |
| DE_ASSERT(m_test_iterations.size() > n_iteration); |
| |
| const _test_iteration& test_iteration = m_test_iterations[n_iteration]; |
| |
| return TessellationShaderUtils::getGenericTECode(test_iteration.vertex_spacing, test_iteration.primitive_mode, |
| TESSELLATION_SHADER_VERTEX_ORDERING_CCW, false); /* point mode */ |
| } |
| |
| /** Initializes test iterations for the test. The following modes and inner/outer tess level |
| * configurations are used to form the test set: |
| * |
| * - Inner/outer tessellation level combinations as returned by |
| * TessellationShaderUtils::getTessellationLevelSetForPrimitiveMode() |
| * - All primitive modes; |
| * - All vertex spacing modes; |
| * |
| * All permutations are used to generate the test set. |
| **/ |
| void TessellationShaderInvarianceRule3Test::initTestIterations() |
| { |
| DE_ASSERT(m_test_iterations.size() == 0); |
| |
| /* Retrieve GL_MAX_TESS_GEN_LEVEL_EXT value */ |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| 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"); |
| |
| /* Iterate through all primitive and vertex spacing modes */ |
| _tessellation_primitive_mode primitive_modes[] = { TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES, |
| TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS, |
| TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES }; |
| _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_primitive_modes = sizeof(primitive_modes) / sizeof(primitive_modes[0]); |
| const unsigned int n_vs_modes = sizeof(vs_modes) / sizeof(vs_modes[0]); |
| |
| for (unsigned int n_primitive_mode = 0; n_primitive_mode < n_primitive_modes; ++n_primitive_mode) |
| { |
| _tessellation_primitive_mode primitive_mode = primitive_modes[n_primitive_mode]; |
| |
| 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]; |
| |
| /* Retrieve inner/outer tessellation level combinations we want the tests to be run for */ |
| _tessellation_levels_set levels_set; |
| |
| levels_set = TessellationShaderUtils::getTessellationLevelSetForPrimitiveMode( |
| primitive_mode, gl_max_tess_gen_level_value, |
| TESSELLATION_LEVEL_SET_FILTER_ALL_LEVELS_USE_THE_SAME_VALUE); |
| |
| /* Iterate through all configurations */ |
| for (_tessellation_levels_set_const_iterator levels_iterator = levels_set.begin(); |
| levels_iterator != levels_set.end(); levels_iterator++) |
| { |
| const _tessellation_levels& levels = *levels_iterator; |
| |
| /* Create a test descriptor for all the parameters we now have */ |
| _test_iteration test; |
| |
| memcpy(test.inner_tess_levels, levels.inner, sizeof(test.inner_tess_levels)); |
| memcpy(test.outer_tess_levels, levels.outer, sizeof(test.outer_tess_levels)); |
| |
| test.primitive_mode = primitive_mode; |
| test.vertex_spacing = vs_mode; |
| |
| m_test_iterations.push_back(test); |
| } /* for (all inner/outer tessellation levels) */ |
| } /* for (all vertex spacing modes) */ |
| } /* for (all primitive modes) */ |
| } |
| |
| /** Verifies result data on per-iteration basis. |
| * |
| * Throws TestError exception if an error occurs. |
| * |
| * @param n_iteration Index of iteration the check should be performed for. |
| * @param data Points to array of vec3s storing the vertices as |
| * generated by tessellation |
| **/ |
| void TessellationShaderInvarianceRule3Test::verifyResultDataForIteration(unsigned int n_iteration, const void* data) |
| { |
| DE_ASSERT(m_test_iterations.size() > n_iteration); |
| |
| const glw::GLfloat* data_float = (const glw::GLfloat*)data; |
| const _test_iteration& test_iteration = m_test_iterations[n_iteration]; |
| |
| /* Iterate through all generated vertices.. */ |
| for (unsigned int n_vertex = 0; n_vertex < test_iteration.n_vertices; ++n_vertex) |
| { |
| _vertex expected_vertex; |
| const glw::GLfloat* vertex_data = data_float + 3 /* components */ * n_vertex; |
| |
| expected_vertex.u = -1.0f; |
| expected_vertex.v = -1.0f; |
| expected_vertex.w = -1.0f; |
| |
| /* Make sure that for each vertex, the following language from the extension |
| * spec is followed: |
| */ |
| switch (test_iteration.primitive_mode) |
| { |
| case TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES: |
| { |
| /* For isoline tessellation, if it generates vertices at (0,x) and (1,x) |
| * where <x> is not zero, it will also generate vertices at exactly (0,1-x) |
| * and (1,1-x), respectively. |
| */ |
| if (vertex_data[0] == 0.0f && vertex_data[1] != 0.0f) |
| { |
| expected_vertex.u = vertex_data[0]; |
| expected_vertex.v = 1.0f - vertex_data[1]; |
| expected_vertex.w = -1.0f; |
| } |
| else if (vertex_data[0] == 1.0f && vertex_data[1] != 0.0f) |
| { |
| expected_vertex.u = vertex_data[0]; |
| expected_vertex.v = 1.0f - vertex_data[1]; |
| expected_vertex.w = -1.0f; |
| } |
| |
| break; |
| } /* case TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES: */ |
| |
| case TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS: |
| { |
| /* For quad tessellation, if the subdivision generates a vertex with |
| * coordinates of (x,0) or (0,x), it will also generate a vertex with |
| * coordinates of exactly (1-x,0) or (0,1-x), respectively. |
| */ |
| if (vertex_data[0] != 0.0f && vertex_data[1] == 0.0f) |
| { |
| expected_vertex.u = 1.0f - vertex_data[0]; |
| expected_vertex.v = vertex_data[1]; |
| expected_vertex.w = -1.0f; |
| } |
| else if (vertex_data[0] == 0.0f && vertex_data[1] != 0.0f) |
| { |
| expected_vertex.u = vertex_data[0]; |
| expected_vertex.v = 1.0f - vertex_data[1]; |
| expected_vertex.w = -1.0f; |
| } |
| |
| break; |
| } /* case TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS: */ |
| |
| case TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES: |
| { |
| /* For triangle tessellation, if the subdivision generates a vertex with |
| * tessellation coordinates of the form (0,x,1-x), (x,0,1-x), or (x,1-x,0), |
| * it will also generate a vertex with coordinates of exactly (0,1-x,x), |
| * (1-x,0,x), or (1-x,x,0), respectively. |
| */ |
| if (vertex_data[0] == 0.0f && vertex_data[1] != 0.0f && vertex_data[2] == (1.0f - vertex_data[1])) |
| { |
| expected_vertex.u = vertex_data[0]; |
| expected_vertex.v = vertex_data[2]; |
| expected_vertex.w = vertex_data[1]; |
| } |
| else if (vertex_data[0] != 0.0f && vertex_data[1] == 0.0f && vertex_data[2] == (1.0f - vertex_data[0])) |
| { |
| expected_vertex.u = vertex_data[2]; |
| expected_vertex.v = vertex_data[1]; |
| expected_vertex.w = vertex_data[0]; |
| } |
| else if (vertex_data[0] != 0.0f && vertex_data[1] == (1.0f - vertex_data[0]) && vertex_data[2] == 0.0f) |
| { |
| expected_vertex.u = vertex_data[1]; |
| expected_vertex.v = vertex_data[0]; |
| expected_vertex.w = vertex_data[2]; |
| } |
| |
| break; |
| } /* case TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES: */ |
| |
| default: |
| { |
| TCU_FAIL("Primitive mode unrecognized"); |
| } |
| } /* switch (test_iteration.primitive_mode) */ |
| |
| /* If any of the "expected vertex"'s components is no longer negative, |
| * make sure the vertex can be found in the result data */ |
| if (expected_vertex.u >= 0.0f || expected_vertex.v >= 0.0f || expected_vertex.w >= 0.0f) |
| { |
| bool has_been_found = false; |
| |
| for (unsigned int n_find_vertex = 0; n_find_vertex < test_iteration.n_vertices; ++n_find_vertex) |
| { |
| const glw::GLfloat* current_vertex_data = data_float + 3 /* components */ * n_find_vertex; |
| |
| const glw::GLfloat epsilon = 1e-4f; |
| glw::GLfloat absDelta[3] = { de::abs(current_vertex_data[0] - expected_vertex.u), |
| de::abs(current_vertex_data[1] - expected_vertex.v), |
| de::abs(current_vertex_data[2] - expected_vertex.w) }; |
| |
| if (absDelta[0] < epsilon && absDelta[1] < epsilon && |
| ((expected_vertex.w < 0.0f) || (expected_vertex.w >= 0.0f && absDelta[2] < epsilon))) |
| { |
| has_been_found = true; |
| |
| break; |
| } /* if (the vertex data matches the expected vertex data) */ |
| } /* for (all generated vertices)*/ |
| |
| if (!has_been_found) |
| { |
| TCU_FAIL("Expected symmetrical vertex data was not generated by the tessellator."); |
| } |
| } /* if (any of the components of expected_vertex is no longer negative) */ |
| } /* for (all generated vertices) */ |
| } |
| |
| /** Constructor. |
| * |
| * @param context Rendering context. |
| * |
| **/ |
| TessellationShaderInvarianceRule4Test::TessellationShaderInvarianceRule4Test(Context& context, |
| const ExtParameters& extParams) |
| : TessellationShaderInvarianceBaseTest(context, extParams, "invariance_rule4", |
| "Verifies conformance with fourth invariance rule") |
| { |
| } |
| |
| /** Destructor. */ |
| TessellationShaderInvarianceRule4Test::~TessellationShaderInvarianceRule4Test() |
| { |
| /* Left blank intentionally */ |
| } |
| |
| /** Retrieves amount of iterations the base test implementation should run before |
| * calling global verification routine. |
| * |
| * @return A value that depends on initTestIterations() behavior. |
| **/ |
| unsigned int TessellationShaderInvarianceRule4Test::getAmountOfIterations() |
| { |
| if (m_test_iterations.size() == 0) |
| { |
| initTestIterations(); |
| } |
| |
| return (unsigned int)m_test_iterations.size(); |
| } |
| |
| /** Retrieves iteration-specific tessellation properties. |
| * |
| * @param n_iteration Iteration index to retrieve the properties for. |
| * @param out_inner_tess_levels Deref will be used to store iteration-specific inner |
| * tessellation level values. Must not be NULL. |
| * @param out_outer_tess_levels Deref will be used to store iteration-specific outer |
| * tessellation level values. Must not be NULL. |
| * @param out_point_mode Deref will be used to store iteration-specific flag |
| * telling whether point mode should be enabled for given pass. |
| * Must not be NULL. |
| * @param out_primitive_mode Deref will be used to store iteration-specific primitive |
| * mode. Must not be NULL. |
| * @param out_vertex_ordering Deref will be used to store iteration-specific vertex |
| * ordering. Must not be NULL. |
| * @param out_result_buffer_size Deref will be used to store amount of bytes XFB buffer object |
| * storage should offer for the draw call to succeed. Can |
| * be NULL. |
| **/ |
| void TessellationShaderInvarianceRule4Test::getIterationProperties( |
| unsigned int n_iteration, float* out_inner_tess_levels, float* out_outer_tess_levels, bool* out_point_mode, |
| _tessellation_primitive_mode* out_primitive_mode, _tessellation_shader_vertex_ordering* out_vertex_ordering, |
| unsigned int* out_result_buffer_size) |
| { |
| DE_ASSERT(m_test_iterations.size() > n_iteration); |
| |
| _test_iteration& test_iteration = m_test_iterations[n_iteration]; |
| |
| memcpy(out_inner_tess_levels, test_iteration.inner_tess_levels, sizeof(test_iteration.inner_tess_levels)); |
| memcpy(out_outer_tess_levels, test_iteration.outer_tess_levels, sizeof(test_iteration.outer_tess_levels)); |
| |
| *out_point_mode = false; |
| *out_primitive_mode = test_iteration.primitive_mode; |
| *out_vertex_ordering = TESSELLATION_SHADER_VERTEX_ORDERING_CCW; |
| |
| if (out_result_buffer_size != DE_NULL) |
| { |
| *out_result_buffer_size = m_utils_ptr->getAmountOfVerticesGeneratedByTessellator( |
| *out_primitive_mode, out_inner_tess_levels, out_outer_tess_levels, test_iteration.vertex_spacing, |
| *out_point_mode); |
| test_iteration.n_vertices = *out_result_buffer_size; |
| *out_result_buffer_size = |
| static_cast<unsigned int>(*out_result_buffer_size * 3 /* components */ * sizeof(float)); |
| |
| DE_ASSERT(*out_result_buffer_size != 0); |
| } |
| } |
| |
| /** Retrieves iteration-specific tessellation evaluation shader code. |
| * |
| * @param n_iteration Iteration index, for which the source code is being obtained. |
| * |
| * @return Requested source code. |
| **/ |
| std::string TessellationShaderInvarianceRule4Test::getTECode(unsigned int n_iteration) |
| { |
| DE_ASSERT(m_test_iterations.size() > n_iteration); |
| |
| const _test_iteration& test_iteration = m_test_iterations[n_iteration]; |
| |
| return TessellationShaderUtils::getGenericTECode(test_iteration.vertex_spacing, test_iteration.primitive_mode, |
| TESSELLATION_SHADER_VERTEX_ORDERING_CCW, false); /* point mode */ |
| } |
| |
| /** Initializes test iterations for the test. The following modes and inner/outer tess level |
| * configurations are used to form the test set: |
| * |
| * - Inner/outer tessellation level combinations as returned by |
| * TessellationShaderUtils::getTessellationLevelSetForPrimitiveMode() |
| * - 'Quads' and 'Triangles' primitive modes; |
| * - All vertex spacing modes; |
| * |
| * All permutations are used to generate the test set. |
| **/ |
| void TessellationShaderInvarianceRule4Test::initTestIterations() |
| { |
| DE_ASSERT(m_test_iterations.size() == 0); |
| |
| /* Retrieve GL_MAX_TESS_GEN_LEVEL_EXT value */ |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| 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"); |
| |
| /* Iterate through all primitive and vertex spacing modes relevant to the test */ |
| _tessellation_primitive_mode primitive_modes[] = { TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS, |
| TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES }; |
| _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_primitive_modes = sizeof(primitive_modes) / sizeof(primitive_modes[0]); |
| const unsigned int n_vs_modes = sizeof(vs_modes) / sizeof(vs_modes[0]); |
| |
| for (unsigned int n_primitive_mode = 0; n_primitive_mode < n_primitive_modes; ++n_primitive_mode) |
| { |
| _tessellation_primitive_mode primitive_mode = primitive_modes[n_primitive_mode]; |
| |
| 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]; |
| |
| /* Retrieve inner/outer tessellation level combinations we want the tests to be run for */ |
| _tessellation_levels_set levels; |
| |
| levels = TessellationShaderUtils::getTessellationLevelSetForPrimitiveMode( |
| primitive_mode, gl_max_tess_gen_level_value, |
| TESSELLATION_LEVEL_SET_FILTER_ALL_LEVELS_USE_THE_SAME_VALUE); |
| |
| /* Iterate through all configurations */ |
| for (_tessellation_levels_set_const_iterator levels_iterator = levels.begin(); |
| levels_iterator != levels.end(); levels_iterator++) |
| { |
| const _tessellation_levels& current_levels = *levels_iterator; |
| |
| /* Create a test descriptor for all the parameters we now have. |
| * |
| * The set we're operating on uses different outer tessellation level values, so we |
| * need to make sure the levels are set to the same FP values in order for the test |
| * to succeed. */ |
| _test_iteration test; |
| |
| memcpy(test.inner_tess_levels, current_levels.inner, sizeof(test.inner_tess_levels)); |
| |
| for (int n = 0; n < 4 /* outer tess levels */; ++n) |
| { |
| test.outer_tess_levels[n] = current_levels.outer[0]; |
| } |
| |
| test.primitive_mode = primitive_mode; |
| test.vertex_spacing = vs_mode; |
| |
| m_test_iterations.push_back(test); |
| } /* for (all inner/outer tessellation levels) */ |
| } /* for (all vertex spacing modes) */ |
| } /* for (all primitive modes) */ |
| } |
| |
| /** Verifies user-provided vertex data can be found in the provided vertex data array. |
| * |
| * @param vertex_data Vertex data array the requested vertex data are to be found in. |
| * @param n_vertices Amount of vertices declared in @param vertex_data; |
| * @param vertex_data_seeked Vertex data to be found in @param vertex_data; |
| * @param n_vertex_data_seeked_components Amount of components to take into account. |
| * |
| * @return true if the vertex data was found, false otherwise. |
| **/ |
| bool TessellationShaderInvarianceRule4Test::isVertexDefined(const float* vertex_data, unsigned int n_vertices, |
| const float* vertex_data_seeked, |
| unsigned int n_vertex_data_seeked_components) |
| { |
| bool result = false; |
| |
| DE_ASSERT(n_vertex_data_seeked_components >= 2); |
| |
| for (unsigned int n_vertex = 0; n_vertex < n_vertices; ++n_vertex) |
| { |
| const float* current_vertex_data = vertex_data + 3 /* components */ * n_vertex; |
| |
| if ((vertex_data_seeked[0] == current_vertex_data[0]) && (vertex_data_seeked[1] == current_vertex_data[1]) && |
| ((n_vertex_data_seeked_components < 3) || |
| (n_vertex_data_seeked_components >= 3 && (vertex_data_seeked[2] == current_vertex_data[2])))) |
| { |
| result = true; |
| |
| break; |
| } /* if (components match) */ |
| } /* for (all vertices) */ |
| |
| return result; |
| } |
| |
| /** Verifies result data on per-iteration basis. |
| * |
| * Throws TestError exception if an error occurs. |
| * |
| * @param n_iteration Index of iteration the check should be performed for. |
| * @param data Points to array of vec3s storing the vertices as |
| * generated by tessellation |
| **/ |
| void TessellationShaderInvarianceRule4Test::verifyResultDataForIteration(unsigned int n_iteration, const void* data) |
| { |
| DE_ASSERT(m_test_iterations.size() > n_iteration); |
| |
| const glw::GLfloat* data_float = (const glw::GLfloat*)data; |
| const _test_iteration& test_iteration = m_test_iterations[n_iteration]; |
| |
| /* Iterate through all generated vertices.. */ |
| for (unsigned int n_vertex = 0; n_vertex < test_iteration.n_vertices; ++n_vertex) |
| { |
| std::vector<_vertex> expected_vertices; |
| const glw::GLfloat* vertex_data = data_float + 3 /* components */ * n_vertex; |
| |
| /* Make sure that for each vertex, the following language from the extension |
| * spec is followed: |
| */ |
| switch (test_iteration.primitive_mode) |
| { |
| case TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS: |
| { |
| /* For quad tessellation, if vertices at (x,0) and (1-x,0) are generated |
| * when subdividing the v==0 edge, vertices must be generated at (0,x) and |
| * (0,1-x) when subdividing an otherwise identical u==0 edge. |
| */ |
| if (vertex_data[0] != 0.0f && vertex_data[1] == 0.0f) |
| { |
| const float paired_vertex_data[] = { 1.0f - vertex_data[0], vertex_data[1] }; |
| |
| if (isVertexDefined(data_float, test_iteration.n_vertices, paired_vertex_data, 2)) /* components */ |
| { |
| _vertex expected_vertex; |
| |
| /* First expected vertex */ |
| expected_vertex.u = vertex_data[1]; |
| expected_vertex.v = vertex_data[0]; |
| expected_vertex.w = -1.0f; |
| |
| expected_vertices.push_back(expected_vertex); |
| |
| /* The other expected vertex */ |
| expected_vertex.u = vertex_data[1]; |
| expected_vertex.v = 1.0f - vertex_data[0]; |
| |
| expected_vertices.push_back(expected_vertex); |
| } /* if (the other required vertex is defined) */ |
| } /* if (the first required vertex is defined) */ |
| |
| break; |
| } /* case TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS: */ |
| |
| case TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES: |
| { |
| /* For triangular tessellation, if vertices at (x,1-x,0) and (1-x,x,0) are |
| * generated when subdividing the w==0 edge, vertices must be generated at |
| * (x,0,1-x) and (1-x,0,x) when subdividing an otherwise identical v==0 |
| * edge. |
| */ |
| if (vertex_data[0] != 0.0f && vertex_data[1] == (1.0f - vertex_data[0]) && vertex_data[2] == 0) |
| { |
| const float paired_vertex_data[] = { vertex_data[1], vertex_data[0], vertex_data[2] }; |
| |
| if (isVertexDefined(data_float, test_iteration.n_vertices, paired_vertex_data, 3)) /* components */ |
| { |
| _vertex expected_vertex; |
| |
| /* First expected vertex */ |
| expected_vertex.u = vertex_data[0]; |
| expected_vertex.v = vertex_data[2]; |
| expected_vertex.w = vertex_data[1]; |
| |
| expected_vertices.push_back(expected_vertex); |
| |
| /* The other expected vertex */ |
| expected_vertex.u = vertex_data[1]; |
| expected_vertex.v = vertex_data[2]; |
| expected_vertex.w = vertex_data[0]; |
| |
| expected_vertices.push_back(expected_vertex); |
| } /* if (the other required vertex is defined) */ |
| } /* if (the firsst required vertex is defined) */ |
| |
| break; |
| } /* case TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES: */ |
| |
| default: |
| { |
| TCU_FAIL("Primitive mode unrecognized"); |
| } |
| } /* switch (test_iteration.primitive_mode) */ |
| |
| /* Iterate through all expected vertices */ |
| for (std::vector<_vertex>::const_iterator expected_vertex_iterator = expected_vertices.begin(); |
| expected_vertex_iterator != expected_vertices.end(); expected_vertex_iterator++) |
| { |
| const _vertex& expected_vertex = *expected_vertex_iterator; |
| const float expected_vertex_raw[] = { expected_vertex.u, expected_vertex.v, expected_vertex.w }; |
| bool has_been_found = false; |
| |
| has_been_found = isVertexDefined(data_float, test_iteration.n_vertices, expected_vertex_raw, |
| (expected_vertex.w < 0) ? 2 : 3); |
| |
| if (!has_been_found) |
| { |
| std::stringstream expected_vertex_sstream; |
| |
| expected_vertex_sstream << expected_vertex.u << ", " << expected_vertex.v; |
| |
| if (expected_vertex.w >= 0.0f) |
| { |
| expected_vertex_sstream << ", " << expected_vertex.w; |
| } |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << "For primitive mode:" |
| << "[" |
| << TessellationShaderUtils::getESTokenForPrimitiveMode(test_iteration.primitive_mode) |
| << "]" |
| << "and vertex spacing mode:" |
| << "[" << TessellationShaderUtils::getESTokenForVertexSpacingMode( |
| test_iteration.vertex_spacing) |
| << "]" |
| << " and inner tessellation levels:[" << test_iteration.inner_tess_levels[0] << ", " |
| << test_iteration.inner_tess_levels[1] << ") " |
| << " and outer tessellation levels:[" << test_iteration.outer_tess_levels[0] << ", " |
| << test_iteration.outer_tess_levels[1] << ", " << test_iteration.outer_tess_levels[2] |
| << ", " << test_iteration.outer_tess_levels[3] << ") " |
| << " the following vertex was expected:[" << expected_vertex_sstream.str().c_str() |
| << "] but was not found in the tessellated cooordinate data set" |
| << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL("Expected symmetrical vertex data was not generated by the tessellator."); |
| } /* if (the expected vertex data was not found) */ |
| } /* for (all expected vertices) */ |
| } /* for (all generated vertices) */ |
| } |
| |
| /** Constructor. |
| * |
| * @param context Rendering context. |
| * |
| **/ |
| TessellationShaderInvarianceRule5Test::TessellationShaderInvarianceRule5Test(Context& context, |
| const ExtParameters& extParams) |
| : TessellationShaderInvarianceBaseTest(context, extParams, "invariance_rule5", |
| "Verifies conformance with fifth invariance rule") |
| { |
| } |
| |
| /** Destructor. */ |
| TessellationShaderInvarianceRule5Test::~TessellationShaderInvarianceRule5Test() |
| { |
| /* Left blank intentionally */ |
| } |
| |
| /** Retrieves amount of iterations the base test implementation should run before |
| * calling global verification routine. |
| * |
| * @return A value that depends on initTestIterations() behavior. |
| **/ |
| unsigned int TessellationShaderInvarianceRule5Test::getAmountOfIterations() |
| { |
| if (m_test_quads_iterations.size() == 0 || m_test_triangles_iterations.size() == 0) |
| { |
| initTestIterations(); |
| } |
| |
| return (unsigned int)(m_test_quads_iterations.size() + m_test_triangles_iterations.size()); |
| } |
| |
| /** Retrieves _test_iteration instance specific for user-specified iteration index. |
| * |
| * @param n_iteration Iteration index to retrieve _test_iteration instance for. |
| * |
| * @return Iteration-specific _test_iteration instance. |
| * |
| **/ |
| TessellationShaderInvarianceRule5Test::_test_iteration& TessellationShaderInvarianceRule5Test::getTestForIteration( |
| unsigned int n_iteration) |
| { |
| unsigned int n_triangles_tests = (unsigned int)m_test_triangles_iterations.size(); |
| _test_iteration& test_iteration = (n_iteration < n_triangles_tests) ? |
| m_test_triangles_iterations[n_iteration] : |
| m_test_quads_iterations[n_iteration - n_triangles_tests]; |
| |
| return test_iteration; |
| } |
| |
| /** Retrieves iteration-specific tessellation properties. |
| * |
| * @param n_iteration Iteration index to retrieve the properties for. |
| * @param out_inner_tess_levels Deref will be used to store iteration-specific inner |
| * tessellation level values. Must not be NULL. |
| * @param out_outer_tess_levels Deref will be used to store iteration-specific outer |
| * tessellation level values. Must not be NULL. |
| * @param out_point_mode Deref will be used to store iteration-specific flag |
| * telling whether point mode should be enabled for given pass. |
| * Must not be NULL. |
| * @param out_primitive_mode Deref will be used to store iteration-specific primitive |
| * mode. Must not be NULL. |
| * @param out_vertex_ordering Deref will be used to store iteration-specific vertex |
| * ordering. Must not be NULL. |
| * @param out_result_buffer_size Deref will be used to store amount of bytes XFB buffer object |
| * storage should offer for the draw call to succeed. Can |
| * be NULL. |
| **/ |
| void TessellationShaderInvarianceRule5Test::getIterationProperties( |
| unsigned int n_iteration, float* out_inner_tess_levels, float* out_outer_tess_levels, bool* out_point_mode, |
| _tessellation_primitive_mode* out_primitive_mode, _tessellation_shader_vertex_ordering* out_vertex_ordering, |
| unsigned int* out_result_buffer_size) |
| { |
| DE_ASSERT(m_test_triangles_iterations.size() + m_test_quads_iterations.size() > n_iteration); |
| |
| _test_iteration& test_iteration = getTestForIteration(n_iteration); |
| |
| memcpy(out_inner_tess_levels, test_iteration.inner_tess_levels, sizeof(test_iteration.inner_tess_levels)); |
| memcpy(out_outer_tess_levels, test_iteration.outer_tess_levels, sizeof(test_iteration.outer_tess_levels)); |
| |
| *out_point_mode = false; |
| *out_primitive_mode = test_iteration.primitive_mode; |
| *out_vertex_ordering = test_iteration.vertex_ordering; |
| |
| if (out_result_buffer_size != DE_NULL) |
| { |
| *out_result_buffer_size = m_utils_ptr->getAmountOfVerticesGeneratedByTessellator( |
| *out_primitive_mode, out_inner_tess_levels, out_outer_tess_levels, TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, |
| *out_point_mode); |
| test_iteration.n_vertices = *out_result_buffer_size; |
| *out_result_buffer_size = |
| static_cast<unsigned int>(*out_result_buffer_size * 3 /* components */ * sizeof(float)); |
| |
| DE_ASSERT(*out_result_buffer_size != 0); |
| } |
| } |
| |
| /** Retrieves iteration-specific tessellation evaluation shader code. |
| * |
| * @param n_iteration Iteration index, for which the source code is being obtained. |
| * |
| * @return Requested source code. |
| **/ |
| std::string TessellationShaderInvarianceRule5Test::getTECode(unsigned int n_iteration) |
| { |
| DE_ASSERT(m_test_triangles_iterations.size() + m_test_quads_iterations.size() > n_iteration); |
| |
| const _test_iteration& test_iteration = getTestForIteration(n_iteration); |
| |
| return TessellationShaderUtils::getGenericTECode(TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, |
| test_iteration.primitive_mode, test_iteration.vertex_ordering, |
| false); /* point mode */ |
| } |
| |
| /** Initializes test iterations for the test. The following modes and inner/outer tess level |
| * configurations are used to form the test set: |
| * |
| * - Last inner/outer tessellation level combination as returned by |
| * TessellationShaderUtils::getTessellationLevelSetForPrimitiveMode() |
| * - All primitive modes; |
| * - All vertex spacing modes; |
| * |
| * All permutations are used to generate the test set. |
| **/ |
| void TessellationShaderInvarianceRule5Test::initTestIterations() |
| { |
| DE_ASSERT(m_test_quads_iterations.size() == 0 && m_test_triangles_iterations.size() == 0); |
| |
| /* Retrieve GL_MAX_TESS_GEN_LEVEL_EXT value */ |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| 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"); |
| |
| /* Iterate through all primitive and vertex spacing modes relevant to the test */ |
| _tessellation_primitive_mode primitive_modes[] = { TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS, |
| TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES }; |
| _tessellation_shader_vertex_ordering vo_modes[] = { |
| TESSELLATION_SHADER_VERTEX_ORDERING_CCW, TESSELLATION_SHADER_VERTEX_ORDERING_CW, |
| }; |
| |
| const unsigned int n_primitive_modes = sizeof(primitive_modes) / sizeof(primitive_modes[0]); |
| const unsigned int n_vo_modes = sizeof(vo_modes) / sizeof(vo_modes[0]); |
| |
| for (unsigned int n_primitive_mode = 0; n_primitive_mode < n_primitive_modes; ++n_primitive_mode) |
| { |
| _tessellation_primitive_mode primitive_mode = primitive_modes[n_primitive_mode]; |
| |
| for (unsigned int n_vo_mode = 0; n_vo_mode < n_vo_modes; ++n_vo_mode) |
| { |
| _tessellation_shader_vertex_ordering vertex_ordering = vo_modes[n_vo_mode]; |
| |
| /* Retrieve inner/outer tessellation level combinations we want the tests to be run for */ |
| _tessellation_levels_set levels_set; |
| |
| levels_set = TessellationShaderUtils::getTessellationLevelSetForPrimitiveMode( |
| primitive_mode, gl_max_tess_gen_level_value, |
| TESSELLATION_LEVEL_SET_FILTER_ALL_LEVELS_USE_THE_SAME_VALUE); |
| |
| /* Only use the last inner/outer level configuration, as reported by the utils function. */ |
| const _tessellation_levels& levels = levels_set[levels_set.size() - 1]; |
| |
| /* Create a test descriptor for all the parameters we now have */ |
| _test_iteration test; |
| |
| memcpy(test.inner_tess_levels, levels.inner, sizeof(test.inner_tess_levels)); |
| memcpy(test.outer_tess_levels, levels.outer, sizeof(test.outer_tess_levels)); |
| |
| test.primitive_mode = primitive_mode; |
| test.vertex_ordering = vertex_ordering; |
| |
| if (primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES) |
| { |
| m_test_triangles_iterations.push_back(test); |
| } |
| else |
| { |
| m_test_quads_iterations.push_back(test); |
| } |
| } /* for (all vertex spacing modes) */ |
| } /* for (all primitive modes) */ |
| } |
| |
| /** Verifies user-provided vertex data can be found in the provided vertex data array. |
| * |
| * @param vertex_data Vertex data array the requested vertex data are to be found in. |
| * @param n_vertices Amount of vertices declared in @param vertex_data; |
| * @param vertex_data_seeked Vertex data to be found in @param vertex_data; |
| * @param n_vertex_data_seeked_components Amount of components to take into account. |
| * |
| * @return true if the vertex data was found, false otherwise. |
| **/ |
| bool TessellationShaderInvarianceRule5Test::isVertexDefined(const float* vertex_data, unsigned int n_vertices, |
| const float* vertex_data_seeked, |
| unsigned int n_vertex_data_seeked_components) |
| { |
| const float epsilon = 1e-5f; |
| bool result = false; |
| |
| DE_ASSERT(n_vertex_data_seeked_components >= 2); |
| |
| for (unsigned int n_vertex = 0; n_vertex < n_vertices; ++n_vertex) |
| { |
| const float* current_vertex_data = vertex_data + 3 /* components */ * n_vertex; |
| |
| if (de::abs(vertex_data_seeked[0] - current_vertex_data[0]) < epsilon && |
| de::abs(vertex_data_seeked[1] - current_vertex_data[1]) < epsilon && |
| ((n_vertex_data_seeked_components < 3) || |
| (n_vertex_data_seeked_components >= 3 && |
| de::abs(vertex_data_seeked[2] - current_vertex_data[2]) < epsilon))) |
| { |
| result = true; |
| |
| break; |
| } /* if (components match) */ |
| } /* for (all vertices) */ |
| |
| return result; |
| } |
| |
| /** Verifies result data. Accesses data generated by all iterations. |
| * |
| * Throws TestError exception if an error occurs. |
| * |
| * @param all_iterations_data An array of pointers to buffers, holding gl_TessCoord |
| * data generated by subsequent iterations. |
| **/ |
| void TessellationShaderInvarianceRule5Test::verifyResultData(const void** all_iterations_data) |
| { |
| /* Run two separate iterations: |
| * |
| * a) triangles |
| * b) quads |
| */ |
| for (unsigned int n_iteration = 0; n_iteration < 2 /* quads, triangles */; ++n_iteration) |
| { |
| const unsigned int n_base_iteration = |
| (n_iteration == 0) ? 0 : (unsigned int)m_test_triangles_iterations.size(); |
| const unsigned int set_size = (n_iteration == 0) ? (unsigned int)m_test_triangles_iterations.size() : |
| (unsigned int)m_test_quads_iterations.size(); |
| const _test_iterations& test_iterations = |
| (n_iteration == 0) ? m_test_triangles_iterations : m_test_quads_iterations; |
| |
| DE_ASSERT(test_iterations.size() != 0); |
| |
| /* For each iteration, verify that all vertices generated for all three vertex spacing modes. |
| * are exactly the same (but in different order) */ |
| const float* base_vertex_data = (const float*)all_iterations_data[n_base_iteration + 0]; |
| |
| for (unsigned int n_set = 1; n_set < set_size; ++n_set) |
| { |
| const float* set_vertex_data = (const float*)all_iterations_data[n_base_iteration + n_set]; |
| |
| /* Amount of vertices should not differ between sets */ |
| DE_ASSERT(test_iterations[0].n_vertices == test_iterations[n_set].n_vertices); |
| |
| /* Run through all vertices in base set and make sure they can be found in currently |
| * processed set */ |
| for (unsigned int n_base_vertex = 0; n_base_vertex < test_iterations[0].n_vertices; ++n_base_vertex) |
| { |
| const float* base_vertex = base_vertex_data + 3 /* components */ * n_base_vertex; |
| |
| if (!isVertexDefined(set_vertex_data, test_iterations[n_set].n_vertices, base_vertex, |
| 3)) /* components */ |
| { |
| const char* primitive_mode = (n_iteration == 0) ? "triangles" : "quads"; |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << "For primitive mode [" << primitive_mode << "] " |
| << "a vertex with tessellation coordinates:[" << base_vertex[0] << ", " |
| << base_vertex[1] << ", " << base_vertex[2] << ") " |
| << "could not have been found for both vertex orderings." |
| << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL("Implementation does not follow Rule 5."); |
| } |
| } /* for (all base set's vertices) */ |
| } /* for (all sets) */ |
| } /* for (both primitive types) */ |
| } |
| |
| /** Constructor. |
| * |
| * @param context Rendering context. |
| * |
| **/ |
| TessellationShaderInvarianceRule6Test::TessellationShaderInvarianceRule6Test(Context& context, |
| const ExtParameters& extParams) |
| : TessellationShaderInvarianceBaseTest(context, extParams, "invariance_rule6", |
| "Verifies conformance with sixth invariance rule") |
| { |
| } |
| |
| /** Destructor. */ |
| TessellationShaderInvarianceRule6Test::~TessellationShaderInvarianceRule6Test() |
| { |
| /* Left blank intentionally */ |
| } |
| |
| /** Retrieves amount of iterations the base test implementation should run before |
| * calling global verification routine. |
| * |
| * @return A value that depends on initTestIterations() behavior. |
| **/ |
| unsigned int TessellationShaderInvarianceRule6Test::getAmountOfIterations() |
| { |
| if (m_test_quads_iterations.size() == 0 || m_test_triangles_iterations.size() == 0) |
| { |
| initTestIterations(); |
| } |
| |
| return (unsigned int)(m_test_quads_iterations.size() + m_test_triangles_iterations.size()); |
| } |
| |
| /** Retrieves _test_iteration instance specific for user-specified iteration index. |
| * |
| * @param n_iteration Iteration index to retrieve _test_iteration instance for. |
| * |
| * @return Iteration-specific _test_iteration instance. |
| * |
| **/ |
| TessellationShaderInvarianceRule6Test::_test_iteration& TessellationShaderInvarianceRule6Test::getTestForIteration( |
| unsigned int n_iteration) |
| { |
| unsigned int n_triangles_tests = (unsigned int)m_test_triangles_iterations.size(); |
| _test_iteration& test_iteration = (n_iteration < n_triangles_tests) ? |
| m_test_triangles_iterations[n_iteration] : |
| m_test_quads_iterations[n_iteration - n_triangles_tests]; |
| |
| return test_iteration; |
| } |
| |
| /** Retrieves iteration-specific tessellation properties. |
| * |
| * @param n_iteration Iteration index to retrieve the properties for. |
| * @param out_inner_tess_levels Deref will be used to store iteration-specific inner |
| * tessellation level values. Must not be NULL. |
| * @param out_outer_tess_levels Deref will be used to store iteration-specific outer |
| * tessellation level values. Must not be NULL. |
| * @param out_point_mode Deref will be used to store iteration-specific flag |
| * telling whether point mode should be enabled for given pass. |
| * Must not be NULL. |
| * @param out_primitive_mode Deref will be used to store iteration-specific primitive |
| * mode. Must not be NULL. |
| * @param out_vertex_ordering Deref will be used to store iteration-specific vertex |
| * ordering. Must not be NULL. |
| * @param out_result_buffer_size Deref will be used to store amount of bytes XFB buffer object |
| * storage should offer for the draw call to succeed. Can |
| * be NULL. |
| **/ |
| void TessellationShaderInvarianceRule6Test::getIterationProperties( |
| unsigned int n_iteration, float* out_inner_tess_levels, float* out_outer_tess_levels, bool* out_point_mode, |
| _tessellation_primitive_mode* out_primitive_mode, _tessellation_shader_vertex_ordering* out_vertex_ordering, |
| unsigned int* out_result_buffer_size) |
| { |
| DE_ASSERT(m_test_triangles_iterations.size() + m_test_quads_iterations.size() > n_iteration); |
| |
| _test_iteration& test_iteration = getTestForIteration(n_iteration); |
| |
| memcpy(out_inner_tess_levels, test_iteration.inner_tess_levels, sizeof(test_iteration.inner_tess_levels)); |
| memcpy(out_outer_tess_levels, test_iteration.outer_tess_levels, sizeof(test_iteration.outer_tess_levels)); |
| |
| *out_point_mode = false; |
| *out_primitive_mode = test_iteration.primitive_mode; |
| *out_vertex_ordering = test_iteration.vertex_ordering; |
| |
| if (out_result_buffer_size != DE_NULL) |
| { |
| *out_result_buffer_size = m_utils_ptr->getAmountOfVerticesGeneratedByTessellator( |
| *out_primitive_mode, out_inner_tess_levels, out_outer_tess_levels, TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, |
| *out_point_mode); |
| test_iteration.n_vertices = *out_result_buffer_size; |
| *out_result_buffer_size = |
| static_cast<unsigned int>(*out_result_buffer_size * 3 /* components */ * sizeof(float)); |
| |
| DE_ASSERT(*out_result_buffer_size != 0); |
| } |
| } |
| |
| /** Retrieves iteration-specific tessellation evaluation shader code. |
| * |
| * @param n_iteration Iteration index, for which the source code is being obtained. |
| * |
| * @return Requested source code. |
| **/ |
| std::string TessellationShaderInvarianceRule6Test::getTECode(unsigned int n_iteration) |
| { |
| DE_ASSERT(m_test_triangles_iterations.size() + m_test_quads_iterations.size() > n_iteration); |
| |
| const _test_iteration& test_iteration = getTestForIteration(n_iteration); |
| |
| return TessellationShaderUtils::getGenericTECode(TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, |
| test_iteration.primitive_mode, test_iteration.vertex_ordering, |
| false); /* point mode */ |
| } |
| |
| /** Initializes test iterations for the test. The following modes and inner/outer tess level |
| * configurations are used to form the test set: |
| * |
| * - Tessellation level combinations as returned by |
| * TessellationShaderUtils::getTessellationLevelSetForPrimitiveMode() (however, the inner |
| * tessellation level values are all set to values corresponding to last item returned for |
| * the set) |
| * - All primitive modes; |
| * - All vertex ordering modes; |
| * |
| * All permutations are used to generate the test set. |
| **/ |
| void TessellationShaderInvarianceRule6Test::initTestIterations() |
| { |
| DE_ASSERT(m_test_quads_iterations.size() == 0 && m_test_triangles_iterations.size() == 0); |
| |
| /* Retrieve GL_MAX_TESS_GEN_LEVEL_EXT value */ |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| 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"); |
| |
| /* Iterate through all primitive and vertex spacing modes relevant to the test */ |
| _tessellation_primitive_mode primitive_modes[] = { TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES, |
| TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS }; |
| _tessellation_shader_vertex_ordering vertex_ordering_modes[] = { TESSELLATION_SHADER_VERTEX_ORDERING_CCW, |
| TESSELLATION_SHADER_VERTEX_ORDERING_CW }; |
| |
| const unsigned int n_primitive_modes = sizeof(primitive_modes) / sizeof(primitive_modes[0]); |
| const unsigned int n_vo_modes = sizeof(vertex_ordering_modes) / sizeof(vertex_ordering_modes[0]); |
| |
| for (unsigned int n_primitive_mode = 0; n_primitive_mode < n_primitive_modes; ++n_primitive_mode) |
| { |
| _tessellation_primitive_mode primitive_mode = primitive_modes[n_primitive_mode]; |
| |
| for (unsigned int n_vo_mode = 0; n_vo_mode < n_vo_modes; ++n_vo_mode) |
| { |
| _tessellation_shader_vertex_ordering vertex_ordering = vertex_ordering_modes[n_vo_mode]; |
| |
| /* Retrieve inner/outer tessellation level combinations we want the tests to be run for. |
| * Since each level set we will be provided by getTessellationLevelSetForPrimitiveMode() |
| * is unique and does not repeat, we'll just make sure the inner level values are set to |
| * the same set of values, so that the conditions the test must meet are actually met. |
| */ |
| float* inner_levels_to_use = DE_NULL; |
| _tessellation_levels_set levels_set; |
| unsigned int n_levels_sets = 0; |
| |
| levels_set = TessellationShaderUtils::getTessellationLevelSetForPrimitiveMode( |
| primitive_mode, gl_max_tess_gen_level_value, |
| TESSELLATION_LEVEL_SET_FILTER_ALL_LEVELS_USE_THE_SAME_VALUE); |
| |
| n_levels_sets = (unsigned int)levels_set.size(); |
| inner_levels_to_use = levels_set[n_levels_sets - 1].inner; |
| |
| for (unsigned int n_levels_set = 0; n_levels_set < n_levels_sets - 1; n_levels_set++) |
| { |
| /* Make sure the Utils function was not changed and that inner level values |
| * are actually unique across the whole set */ |
| DE_ASSERT(levels_set[n_levels_set].inner[0] != levels_set[n_levels_sets - 1].inner[0] && |
| levels_set[n_levels_set].inner[1] != levels_set[n_levels_sets - 1].inner[1]); |
| |
| /* Force the last set's inner values to all level combinations we'll be using */ |
| memcpy(levels_set[n_levels_set].inner, inner_levels_to_use, sizeof(levels_set[n_levels_set].inner)); |
| } /* for (all sets retrieved from Utils function */ |
| |
| for (_tessellation_levels_set_const_iterator set_iterator = levels_set.begin(); |
| set_iterator != levels_set.end(); set_iterator++) |
| { |
| const _tessellation_levels& levels = *set_iterator; |
| |
| /* Create a test descriptor for all the parameters we now have */ |
| _test_iteration test; |
| |
| memcpy(test.inner_tess_levels, levels.inner, sizeof(test.inner_tess_levels)); |
| memcpy(test.outer_tess_levels, levels.outer, sizeof(test.outer_tess_levels)); |
| |
| test.primitive_mode = primitive_mode; |
| test.vertex_ordering = vertex_ordering; |
| |
| if (primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES) |
| { |
| m_test_triangles_iterations.push_back(test); |
| } |
| else |
| { |
| m_test_quads_iterations.push_back(test); |
| } |
| } /* for (all level sets) */ |
| } /* for (all vertex ordering modes) */ |
| } /* for (all primitive modes) */ |
| } |
| |
| /** Verifies result data. Accesses data generated by all iterations. |
| * |
| * Throws TestError exception if an error occurs. |
| * |
| * @param all_iterations_data An array of pointers to buffers, holding gl_TessCoord |
| * data generated by subsequent iterations. |
| **/ |
| void TessellationShaderInvarianceRule6Test::verifyResultData(const void** all_iterations_data) |
| { |
| /* Run two separate iterations: |
| * |
| * a) triangles |
| * b) quads |
| */ |
| |
| for (unsigned int n_iteration = 0; n_iteration < 2 /* quads, triangles */; ++n_iteration) |
| { |
| const unsigned int n_base_iteration = |
| (n_iteration == 0) ? 0 : (unsigned int)m_test_triangles_iterations.size(); |
| |
| const unsigned int n_sets = (n_iteration == 0) ? (unsigned int)m_test_triangles_iterations.size() : |
| (unsigned int)m_test_quads_iterations.size(); |
| |
| _tessellation_primitive_mode primitive_mode = (n_iteration == 0) ? |
| TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES : |
| TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS; |
| |
| const _test_iterations& test_iterations = |
| (n_iteration == 0) ? m_test_triangles_iterations : m_test_quads_iterations; |
| |
| const unsigned int n_triangles_in_base_set = test_iterations[0].n_vertices / 3 /* vertices per triangle */; |
| |
| DE_ASSERT(test_iterations.size() != 0); |
| |
| /* For each iteration, verify that all vertices generated for all three vertex spacing modes. |
| * are exactly the same (but in different order) */ |
| const _test_iteration& base_test = test_iterations[0]; |
| const float* base_vertex_data = (const float*)all_iterations_data[n_base_iteration + 0]; |
| |
| for (unsigned int n_set = 1; n_set < n_sets; ++n_set) |
| { |
| const _test_iteration& set_test = test_iterations[n_set]; |
| const float* set_vertex_data = (const float*)all_iterations_data[n_base_iteration + n_set]; |
| |
| /* We're operating on triangles so make sure the amount of vertices we're dealing with is |
| * divisible by 3 */ |
| DE_ASSERT((test_iterations[n_set].n_vertices % 3) == 0); |
| |
| const unsigned int n_triangles_in_curr_set = test_iterations[n_set].n_vertices / 3; |
| |
| /* Take base triangles and make sure they can be found in iteration-specific set. |
| * Now, thing to keep in mind here is that we must not assume any specific vertex |
| * and triangle order which is why the slow search. */ |
| for (unsigned int n_base_triangle = 0; n_base_triangle < n_triangles_in_base_set; ++n_base_triangle) |
| { |
| /* Extract base triangle data first */ |
| const float* base_triangle_vertex1 = base_vertex_data + |
| n_base_triangle * 3 * /* vertices per triangle */ |
| 3; /* components */ |
| const float* base_triangle_vertex2 = base_triangle_vertex1 + 3; /* components */ |
| const float* base_triangle_vertex3 = base_triangle_vertex2 + 3; /* components */ |
| |
| /* Only interior triangles should be left intact. Is this an interior triangle? */ |
| if (!TessellationShaderUtils::isOuterEdgeVertex(primitive_mode, base_triangle_vertex1) && |
| !TessellationShaderUtils::isOuterEdgeVertex(primitive_mode, base_triangle_vertex2) && |
| !TessellationShaderUtils::isOuterEdgeVertex(primitive_mode, base_triangle_vertex3)) |
| { |
| /* Iterate through all triangles in considered set */ |
| bool has_base_set_triangle_been_found = false; |
| |
| for (unsigned int n_curr_set_triangle = 0; n_curr_set_triangle < n_triangles_in_curr_set; |
| ++n_curr_set_triangle) |
| { |
| const float* curr_triangle = set_vertex_data + |
| n_curr_set_triangle * 3 * /* vertices per triangle */ |
| 3; /* components */ |
| |
| if (TessellationShaderUtils::isTriangleDefined(base_triangle_vertex1, curr_triangle)) |
| { |
| has_base_set_triangle_been_found = true; |
| |
| break; |
| } |
| } /* for (all triangles in currently processed set) */ |
| |
| if (!has_base_set_triangle_been_found) |
| { |
| std::string primitive_mode_str = |
| TessellationShaderUtils::getESTokenForPrimitiveMode(base_test.primitive_mode); |
| |
| m_testCtx.getLog() |
| << tcu::TestLog::Message << "For primitive mode [" << primitive_mode_str << "]" |
| << ", base inner tessellation levels:" |
| << "[" << base_test.inner_tess_levels[0] << ", " << base_test.inner_tess_levels[1] << "]" |
| << ", base outer tessellation levels:" |
| << "[" << base_test.outer_tess_levels[0] << ", " << base_test.outer_tess_levels[1] << ", " |
| << base_test.outer_tess_levels[2] << ", " << base_test.outer_tess_levels[3] << "]" |
| << ", reference inner tessellation levels:" |
| << "[" << set_test.inner_tess_levels[0] << ", " << set_test.inner_tess_levels[1] << "]" |
| << ", reference outer tessellation levels:" |
| << "[" << set_test.outer_tess_levels[0] << ", " << set_test.outer_tess_levels[1] << ", " |
| << set_test.outer_tess_levels[2] << ", " << set_test.outer_tess_levels[3] << "]" |
| << ", the following triangle formed during base tessellation run was not found in " |
| "reference run:" |
| << "[" << base_triangle_vertex1[0] << ", " << base_triangle_vertex1[1] << ", " |
| << base_triangle_vertex1[2] << "]x" |
| << "[" << base_triangle_vertex2[0] << ", " << base_triangle_vertex2[1] << ", " |
| << base_triangle_vertex2[2] << "]x" |
| << "[" << base_triangle_vertex3[0] << ", " << base_triangle_vertex3[1] << ", " |
| << base_triangle_vertex3[2] |
| |
| << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL("Implementation does not appear to be rule 6-conformant"); |
| } /* if (triangle created during base run was not found in reference run) */ |
| } /* if (base triangle is interior) */ |
| } /* for (all base set's vertices) */ |
| } /* for (all sets) */ |
| } /* for (both primitive types) */ |
| } |
| |
| /** Constructor. |
| * |
| * @param context Rendering context. |
| * |
| **/ |
| TessellationShaderInvarianceRule7Test::TessellationShaderInvarianceRule7Test(Context& context, |
| const ExtParameters& extParams) |
| : TessellationShaderInvarianceBaseTest(context, extParams, "invariance_rule7", |
| "Verifies conformance with seventh invariance rule") |
| { |
| } |
| |
| /** Destructor. */ |
| TessellationShaderInvarianceRule7Test::~TessellationShaderInvarianceRule7Test() |
| { |
| /* Left blank intentionally */ |
| } |
| |
| /** Retrieves amount of iterations the base test implementation should run before |
| * calling global verification routine. |
| * |
| * @return A value that depends on initTestIterations() behavior. |
| **/ |
| unsigned int TessellationShaderInvarianceRule7Test::getAmountOfIterations() |
| { |
| if (m_test_quads_iterations.size() == 0 || m_test_triangles_iterations.size() == 0) |
| { |
| initTestIterations(); |
| } |
| |
| return (unsigned int)(m_test_quads_iterations.size() + m_test_triangles_iterations.size()); |
| } |
| |
| /** Retrieves index of a test iteration that was initialized with user-provided |
| * properties. |
| * |
| * @param is_triangles_iteration true if the seeked test iteration should have |
| * been run for 'triangles' primitive mode', false |
| * if 'quads' primitive mode run is seeked. |
| * @param inner_tess_levels Two FP values describing inner tessellation level |
| * values the seeked run should have used. |
| * @param outer_tess_levels Four FP values describing outer tessellation level |
| * values the seeked run should have used. |
| * @param vertex_ordering Vertex ordering mode the seeked run should have used. |
| * @param n_modified_outer_tess_level Tells which outer tessellation level should be |
| * excluded from checking. |
| * |
| * @return 0xFFFFFFFF if no test iteration was run for user-provided properties, |
| * actual index otherwise. |
| * |
| **/ |
| unsigned int TessellationShaderInvarianceRule7Test::getTestIterationIndex( |
| bool is_triangles_iteration, const float* inner_tess_levels, const float* outer_tess_levels, |
| _tessellation_shader_vertex_ordering vertex_ordering, unsigned int n_modified_outer_tess_level) |
| { |
| const float epsilon = 1e-5f; |
| unsigned int result = 0xFFFFFFFF; |
| const _test_iterations& test_iterations = |
| (is_triangles_iteration) ? m_test_triangles_iterations : m_test_quads_iterations; |
| const unsigned int n_test_iterations = (unsigned int)test_iterations.size(); |
| |
| for (unsigned int n_test_iteration = 0; n_test_iteration < n_test_iterations; ++n_test_iteration) |
| { |
| _test_iteration test_iteration = test_iterations[n_test_iteration]; |
| |
| if (de::abs(test_iteration.inner_tess_levels[0] - inner_tess_levels[0]) < epsilon && |
| de::abs(test_iteration.inner_tess_levels[1] - inner_tess_levels[1]) < epsilon && |
| test_iteration.vertex_ordering == vertex_ordering && |
| test_iteration.n_modified_outer_tess_level == n_modified_outer_tess_level) |
| { |
| /* Only compare outer tessellation levels that have not been modified */ |
| if (((n_modified_outer_tess_level == 0) || |
| (n_modified_outer_tess_level != 0 && |
| de::abs(test_iteration.outer_tess_levels[0] - outer_tess_levels[0]) < epsilon)) && |
| ((n_modified_outer_tess_level == 1) || |
| (n_modified_outer_tess_level != 1 && |
| de::abs(test_iteration.outer_tess_levels[1] - outer_tess_levels[1]) < epsilon)) && |
| ((n_modified_outer_tess_level == 2) || |
| (n_modified_outer_tess_level != 2 && |
| de::abs(test_iteration.outer_tess_levels[2] - outer_tess_levels[2]) < epsilon)) && |
| ((n_modified_outer_tess_level == 3) || |
| (n_modified_outer_tess_level != 3 && |
| de::abs(test_iteration.outer_tess_levels[3] - outer_tess_levels[3]) < epsilon))) |
| { |
| result = n_test_iteration; |
| |
| break; |
| } |
| } |
| } /* for (all test iterations) */ |
| |
| return result; |
| } |
| |
| /** Retrieves _test_iteration instance specific for user-specified iteration index. |
| * |
| * @param n_iteration Iteration index to retrieve _test_iteration instance for. |
| * |
| * @return Iteration-specific _test_iteration instance. |
| * |
| **/ |
| TessellationShaderInvarianceRule7Test::_test_iteration& TessellationShaderInvarianceRule7Test::getTestForIteration( |
| unsigned int n_iteration) |
| { |
| unsigned int n_triangles_tests = (unsigned int)m_test_triangles_iterations.size(); |
| _test_iteration& test_iteration = (n_iteration < n_triangles_tests) ? |
| m_test_triangles_iterations[n_iteration] : |
| m_test_quads_iterations[n_iteration - n_triangles_tests]; |
| |
| return test_iteration; |
| } |
| |
| /** Retrieves iteration-specific tessellation properties. |
| * |
| * @param n_iteration Iteration index to retrieve the properties for. |
| * @param out_inner_tess_levels Deref will be used to store iteration-specific inner |
| * tessellation level values. Must not be NULL. |
| * @param out_outer_tess_levels Deref will be used to store iteration-specific outer |
| * tessellation level values. Must not be NULL. |
| * @param out_point_mode Deref will be used to store iteration-specific flag |
| * telling whether point mode should be enabled for given pass. |
| * Must not be NULL. |
| * @param out_primitive_mode Deref will be used to store iteration-specific primitive |
| * mode. Must not be NULL. |
| * @param out_vertex_ordering Deref will be used to store iteration-specific vertex |
| * ordering. Must not be NULL. |
| * @param out_result_buffer_size Deref will be used to store amount of bytes XFB buffer object |
| * storage should offer for the draw call to succeed. Can |
| * be NULL. |
| **/ |
| void TessellationShaderInvarianceRule7Test::getIterationProperties( |
| unsigned int n_iteration, float* out_inner_tess_levels, float* out_outer_tess_levels, bool* out_point_mode, |
| _tessellation_primitive_mode* out_primitive_mode, _tessellation_shader_vertex_ordering* out_vertex_ordering, |
| unsigned int* out_result_buffer_size) |
| { |
| DE_ASSERT(m_test_triangles_iterations.size() + m_test_quads_iterations.size() > n_iteration); |
| |
| _test_iteration& test_iteration = getTestForIteration(n_iteration); |
| |
| memcpy(out_inner_tess_levels, test_iteration.inner_tess_levels, sizeof(test_iteration.inner_tess_levels)); |
| memcpy(out_outer_tess_levels, test_iteration.outer_tess_levels, sizeof(test_iteration.outer_tess_levels)); |
| |
| *out_point_mode = false; |
| *out_primitive_mode = test_iteration.primitive_mode; |
| *out_vertex_ordering = test_iteration.vertex_ordering; |
| |
| if (out_result_buffer_size != DE_NULL) |
| { |
| *out_result_buffer_size = m_utils_ptr->getAmountOfVerticesGeneratedByTessellator( |
| *out_primitive_mode, out_inner_tess_levels, out_outer_tess_levels, TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, |
| *out_point_mode); |
| test_iteration.n_vertices = *out_result_buffer_size; |
| *out_result_buffer_size = |
| static_cast<unsigned int>(*out_result_buffer_size * 3 /* components */ * sizeof(float)); |
| |
| DE_ASSERT(*out_result_buffer_size != 0); |
| } |
| } |
| |
| /** Retrieves iteration-specific tessellation evaluation shader code. |
| * |
| * @param n_iteration Iteration index, for which the source code is being obtained. |
| * |
| * @return Requested source code. |
| **/ |
| std::string TessellationShaderInvarianceRule7Test::getTECode(unsigned int n_iteration) |
| { |
| DE_ASSERT(m_test_triangles_iterations.size() + m_test_quads_iterations.size() > n_iteration); |
| |
| const _test_iteration& test_iteration = getTestForIteration(n_iteration); |
| |
| return TessellationShaderUtils::getGenericTECode(TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, |
| test_iteration.primitive_mode, test_iteration.vertex_ordering, |
| false); /* point mode */ |
| } |
| |
| /** Initializes test iterations for the test. The following modes and inner/outer tess level |
| * configurations are used to form the test set: |
| * |
| * - All inner/outer tessellation level combinations as returned by |
| * TessellationShaderUtils::getTessellationLevelSetForPrimitiveMode() |
| * times 3 (for triangles) or 4 (for quads). For each combination, |
| * the test will capture tessellation coordinates multiple times, each |
| * time changing a different outer tessellation level value and leaving |
| * the rest intact. |
| * - All primitive modes; |
| * - All vertex spacing modes; |
| * |
| * All permutations are used to generate the test set. |
| **/ |
| void TessellationShaderInvarianceRule7Test::initTestIterations() |
| { |
| DE_ASSERT(m_test_quads_iterations.size() == 0 && m_test_triangles_iterations.size() == 0); |
| |
| /* Retrieve GL_MAX_TESS_GEN_LEVEL_EXT value */ |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| 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"); |
| |
| /* Iterate through all primitive and vertex spacing modes relevant to the test */ |
| _tessellation_primitive_mode primitive_modes[] = { TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS, |
| TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES }; |
| _tessellation_shader_vertex_ordering vo_modes[] = { |
| TESSELLATION_SHADER_VERTEX_ORDERING_CCW, TESSELLATION_SHADER_VERTEX_ORDERING_CW, |
| }; |
| |
| const unsigned int n_primitive_modes = sizeof(primitive_modes) / sizeof(primitive_modes[0]); |
| const unsigned int n_vo_modes = sizeof(vo_modes) / sizeof(vo_modes[0]); |
| |
| for (unsigned int n_primitive_mode = 0; n_primitive_mode < n_primitive_modes; ++n_primitive_mode) |
| { |
| _tessellation_primitive_mode primitive_mode = primitive_modes[n_primitive_mode]; |
| const unsigned int n_relevant_outer_tess_levels = |
| (primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS) ? 4 : 3; |
| |
| for (unsigned int n_vo_mode = 0; n_vo_mode < n_vo_modes; ++n_vo_mode) |
| { |
| _tessellation_shader_vertex_ordering vertex_ordering = vo_modes[n_vo_mode]; |
| |
| /* Retrieve inner/outer tessellation level combinations we want the tests to be run for */ |
| _tessellation_levels_set levels_set; |
| |
| levels_set = TessellationShaderUtils::getTessellationLevelSetForPrimitiveMode( |
| primitive_mode, gl_max_tess_gen_level_value, |
| TESSELLATION_LEVEL_SET_FILTER_ALL_LEVELS_USE_THE_SAME_VALUE); |
| |
| /* Create test descriptor for all inner/outer level configurations we received from the utils function. */ |
| for (_tessellation_levels_set_const_iterator levels_set_iterator = levels_set.begin(); |
| levels_set_iterator != levels_set.end(); levels_set_iterator++) |
| { |
| const _tessellation_levels& levels = *levels_set_iterator; |
| |
| for (unsigned int n_outer_level_to_change = 0; |
| n_outer_level_to_change < n_relevant_outer_tess_levels + 1 /* base iteration */; |
| ++n_outer_level_to_change) |
| { |
| /* Create a test descriptor for all the parameters we now have */ |
| _test_iteration test; |
| |
| memcpy(test.inner_tess_levels, levels.inner, sizeof(test.inner_tess_levels)); |
| memcpy(test.outer_tess_levels, levels.outer, sizeof(test.outer_tess_levels)); |
| |
| test.primitive_mode = primitive_mode; |
| test.vertex_ordering = vertex_ordering; |
| |
| /* Change iteration-specific outer tessellation level to a different value, but only |
| * if we're not preparing a base iteration*/ |
| if (n_outer_level_to_change != n_relevant_outer_tess_levels) |
| { |
| test.n_modified_outer_tess_level = n_outer_level_to_change; |
| test.outer_tess_levels[n_outer_level_to_change] = (float)(gl_max_tess_gen_level_value) / 3.0f; |
| } |
| else |
| { |
| test.is_base_iteration = true; |
| } |
| |
| if (primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES) |
| { |
| m_test_triangles_iterations.push_back(test); |
| } |
| else |
| { |
| m_test_quads_iterations.push_back(test); |
| } |
| } |
| } /* for (all levels set entries) */ |
| } /* for (all vertex spacing modes) */ |
| } /* for (all primitive modes) */ |
| } |
| |
| /** Tells whether a triangle is included in user-provided set of triangles. |
| * The triangle is expected to use an undefined vertex ordering. |
| * |
| * @param base_triangle_data 9 FP values defining 3 vertices of a triangle. |
| * @param vertex_data Vertex stream. It is expected these vertices |
| * form triangles. It is also assumed each vertex |
| * is expressed with 3 components. |
| * @param vertex_data_n_vertices Amount of vertices that can be found in @param |
| * vertex_data |
| * |
| * @return true if the triangle was found in user-provided triangle set, |
| * false otherwise. |
| * |
| **/ |
| bool TessellationShaderInvarianceRule7Test::isTriangleDefinedInVertexDataSet(const float* base_triangle_data, |
| const float* vertex_data, |
| unsigned int vertex_data_n_vertices) |
| { |
| bool result = false; |
| |
| for (unsigned int n_triangle = 0; n_triangle < vertex_data_n_vertices / 3 /* vertices per triangle */; n_triangle++) |
| { |
| const float* current_triangle_data = vertex_data + |
| n_triangle * 3 * /* vertices per triangle */ |
| 3; /* components */ |
| |
| if (TessellationShaderUtils::isTriangleDefined(current_triangle_data, base_triangle_data)) |
| { |
| result = true; |
| |
| break; |
| } |
| } /* for (all vertices) */ |
| |
| return result; |
| } |
| |
| /** Verifies result data. Accesses data generated by all iterations. |
| * |
| * Throws TestError exception if an error occurs. |
| * |
| * @param all_iterations_data An array of pointers to buffers, holding gl_TessCoord |
| * data generated by subsequent iterations. |
| **/ |
| void TessellationShaderInvarianceRule7Test::verifyResultData(const void** all_iterations_data) |
| { |
| const float epsilon = 1e-5f; |
| |
| /* Run two separate iterations: |
| * |
| * a) triangles |
| * b) quads |
| */ |
| for (unsigned int n_iteration = 0; n_iteration < 2 /* triangles, quads */; ++n_iteration) |
| { |
| bool is_triangles_iteration = (n_iteration == 0); |
| const unsigned int n_base_iteration = |
| (n_iteration == 0) ? 0 : (unsigned int)m_test_triangles_iterations.size(); |
| const unsigned int n_relevant_outer_tess_levels = (is_triangles_iteration) ? 3 : 4; |
| |
| _tessellation_primitive_mode primitive_mode = (n_iteration == 0) ? |
| TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES : |
| TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS; |
| |
| const _test_iterations& test_iterations = |
| (n_iteration == 0) ? m_test_triangles_iterations : m_test_quads_iterations; |
| |
| DE_ASSERT(test_iterations.size() != 0); |
| |
| /* Find a base iteration first */ |
| for (unsigned int n_base_test_iteration = 0; n_base_test_iteration < test_iterations.size(); |
| n_base_test_iteration++) |
| { |
| const _test_iteration& base_iteration = test_iterations[n_base_test_iteration]; |
| std::vector<int> ref_iteration_indices; |
| |
| if (!base_iteration.is_base_iteration) |
| { |
| continue; |
| } |
| |
| /* Retrieve reference test iterations */ |
| for (unsigned int n_reference_iteration = 0; n_reference_iteration < n_relevant_outer_tess_levels; |
| ++n_reference_iteration) |
| { |
| const unsigned int n_modified_outer_tess_level = |
| (base_iteration.n_modified_outer_tess_level + n_reference_iteration + 1) % |
| n_relevant_outer_tess_levels; |
| const unsigned int ref_iteration_index = getTestIterationIndex( |
| is_triangles_iteration, base_iteration.inner_tess_levels, base_iteration.outer_tess_levels, |
| base_iteration.vertex_ordering, n_modified_outer_tess_level); |
| |
| DE_ASSERT(ref_iteration_index != 0xFFFFFFFF); |
| DE_ASSERT(ref_iteration_index != n_base_test_iteration); |
| |
| ref_iteration_indices.push_back(ref_iteration_index); |
| } |
| |
| /* We can now start comparing base data with the information generated for |
| * reference iterations. */ |
| for (std::vector<int>::const_iterator ref_iteration_iterator = ref_iteration_indices.begin(); |
| ref_iteration_iterator != ref_iteration_indices.end(); ref_iteration_iterator++) |
| { |
| const int& n_ref_test_iteration = *ref_iteration_iterator; |
| const _test_iteration& ref_iteration = test_iterations[n_ref_test_iteration]; |
| |
| /* Now move through all triangles generated for base test iteration. Focus on the ones |
| * that connect the outer edge with one of the inner ones */ |
| const float* base_iteration_vertex_data = |
| (const float*)all_iterations_data[n_base_iteration + n_base_test_iteration]; |
| const float* ref_iteration_vertex_data = |
| (const float*)all_iterations_data[n_base_iteration + n_ref_test_iteration]; |
| |
| for (unsigned int n_base_triangle = 0; |
| n_base_triangle < base_iteration.n_vertices / 3; /* vertices per triangle */ |
| ++n_base_triangle) |
| { |
| const float* base_triangle_data = |
| base_iteration_vertex_data + n_base_triangle * 3 /* vertices */ * 3; /* components */ |
| |
| /* Is that the triangle type we're after? */ |
| const float* base_triangle_vertex1 = base_triangle_data; |
| const float* base_triangle_vertex2 = base_triangle_vertex1 + 3; /* components */ |
| const float* base_triangle_vertex3 = base_triangle_vertex2 + 3; /* components */ |
| bool is_base_triangle_vertex1_outer = |
| TessellationShaderUtils::isOuterEdgeVertex(primitive_mode, base_triangle_vertex1); |
| bool is_base_triangle_vertex2_outer = |
| TessellationShaderUtils::isOuterEdgeVertex(primitive_mode, base_triangle_vertex2); |
| bool is_base_triangle_vertex3_outer = |
| TessellationShaderUtils::isOuterEdgeVertex(primitive_mode, base_triangle_vertex3); |
| unsigned int n_outer_edge_vertices_found = 0; |
| |
| n_outer_edge_vertices_found += (is_base_triangle_vertex1_outer == true); |
| n_outer_edge_vertices_found += (is_base_triangle_vertex2_outer == true); |
| n_outer_edge_vertices_found += (is_base_triangle_vertex3_outer == true); |
| |
| if (n_outer_edge_vertices_found == 0) |
| { |
| /* This is an inner triangle, not really of our interest */ |
| continue; |
| } |
| |
| /* Which outer tessellation level describes the base data edge? */ |
| unsigned int n_base_tess_level = 0; |
| |
| if (primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES) |
| { |
| if ((!is_base_triangle_vertex1_outer || |
| (is_base_triangle_vertex1_outer && base_triangle_vertex1[0] == 0.0f)) && |
| (!is_base_triangle_vertex2_outer || |
| (is_base_triangle_vertex2_outer && base_triangle_vertex2[0] == 0.0f)) && |
| (!is_base_triangle_vertex3_outer || |
| (is_base_triangle_vertex3_outer && base_triangle_vertex3[0] == 0.0f))) |
| { |
| n_base_tess_level = 0; |
| } |
| else if ((!is_base_triangle_vertex1_outer || |
| (is_base_triangle_vertex1_outer && base_triangle_vertex1[1] == 0.0f)) && |
| (!is_base_triangle_vertex2_outer || |
| (is_base_triangle_vertex2_outer && base_triangle_vertex2[1] == 0.0f)) && |
| (!is_base_triangle_vertex3_outer || |
| (is_base_triangle_vertex3_outer && base_triangle_vertex3[1] == 0.0f))) |
| { |
| n_base_tess_level = 1; |
| } |
| else |
| { |
| DE_ASSERT((!is_base_triangle_vertex1_outer || |
| (is_base_triangle_vertex1_outer && base_triangle_vertex1[2] == 0.0f)) && |
| (!is_base_triangle_vertex2_outer || |
| (is_base_triangle_vertex2_outer && base_triangle_vertex2[2] == 0.0f)) && |
| (!is_base_triangle_vertex3_outer || |
| (is_base_triangle_vertex3_outer && base_triangle_vertex3[2] == 0.0f))); |
| |
| n_base_tess_level = 2; |
| } |
| } /* if (primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES) */ |
| else |
| { |
| DE_ASSERT(primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS); |
| |
| std::size_t n_outer_edge_vertices = 0; |
| std::vector<const float*> outer_edge_vertices; |
| |
| if (is_base_triangle_vertex1_outer) |
| { |
| outer_edge_vertices.push_back(base_triangle_vertex1); |
| } |
| |
| if (is_base_triangle_vertex2_outer) |
| { |
| outer_edge_vertices.push_back(base_triangle_vertex2); |
| } |
| |
| if (is_base_triangle_vertex3_outer) |
| { |
| outer_edge_vertices.push_back(base_triangle_vertex3); |
| } |
| |
| n_outer_edge_vertices = outer_edge_vertices.size(); |
| |
| DE_ASSERT(n_outer_edge_vertices >= 1 && n_outer_edge_vertices <= 2); |
| |
| bool is_top_outer_edge = true; |
| bool is_right_outer_edge = true; |
| bool is_bottom_outer_edge = true; |
| bool is_left_outer_edge = true; |
| |
| /* Find which outer edges the vertices don't belong to. If one is in a corner, |
| * the other will clarify to which edge both vertices belong. */ |
| for (unsigned int n_vertex = 0; n_vertex < n_outer_edge_vertices; ++n_vertex) |
| { |
| /* Y < 1, not top outer edge */ |
| if (de::abs(outer_edge_vertices[n_vertex][1] - 1.0f) > epsilon) |
| { |
| is_top_outer_edge = false; |
| } |
| |
| /* X < 1, not right outer edge */ |
| if (de::abs(outer_edge_vertices[n_vertex][0] - 1.0f) > epsilon) |
| { |
| is_right_outer_edge = false; |
| } |
| |
| /* Y > 0, not bottom outer edge */ |
| if (de::abs(outer_edge_vertices[n_vertex][1]) > epsilon) |
| { |
|
|