/*------------------------------------------------------------------------- * 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 "esextcTessellationShaderTessellation.hpp" #include "gluContextInfo.hpp" #include "gluDefs.hpp" #include "glwEnums.hpp" #include "glwFunctions.hpp" #include "tcuTestLog.hpp" #include namespace glcts { /** Vertex shader source code for max_in_out_attributes test. */ const char* TessellationShaderTessellationMaxInOut::m_vs_code = "${VERSION}\n" "\n" "${TESSELLATION_SHADER_REQUIRE}\n" "${SHADER_IO_BLOCKS_ENABLE}\n" "\n" "precision highp float;\n" "\n" "layout(location = 0) in vec4 in_fv;\n" "\n" "out Vertex\n" "{\n" " /* Note: we need to leave some space for gl_Position */\n" " vec4 value[(gl_MaxTessControlInputComponents) / 4 - 1];\n" "} outVertex;\n" "\n" "void main()\n" "{\n" " gl_Position = in_fv;\n" "\n" " for (int i = 0 ; i < (gl_MaxTessControlInputComponents - " "4) / 4 ; i++)\n" /* Max vec4 output attributes - gl_Position */ " {\n" " outVertex.value[i] = vec4(float(4*i), float(4*i) + " "1.0, float(4*i) + 2.0, float(4*i) + 3.0);\n" " }\n" "}\n"; /* Tessellation Control Shader code for max_in_out_attributes test */ const char* TessellationShaderTessellationMaxInOut::m_tcs_code_1 = "${VERSION}\n" "\n" "${TESSELLATION_SHADER_REQUIRE}\n" "\n" "precision highp float;\n" "\n" "layout(vertices = 2) out;\n" "\n" "in Vertex\n" "{\n" " /* Note: we need to leave some space for gl_Position */\n" " vec4 value[(gl_MaxTessControlInputComponents - 4) / 4];\n" "} inVertex[];\n" "\n" "out Vertex\n" "{\n" " /* Note: we need to leave some space for gl_Position */\n" " vec4 value[(gl_MaxTessControlOutputComponents - 4) / 4];\n" "} outVariables[];\n" "\n" "void main()\n" "{\n" " gl_TessLevelInner[0] = 1.0;\n" " gl_TessLevelInner[1] = 1.0;\n" " gl_TessLevelOuter[0] = 1.0;\n" " gl_TessLevelOuter[1] = 1.0;\n" " gl_TessLevelOuter[2] = 1.0;\n" " gl_TessLevelOuter[3] = 1.0;\n" "\n" " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" "\n" " for (int j = 0; j < (gl_MaxTessControlOutputComponents - 4) / 4; j++)\n" " {\n" " outVariables[gl_InvocationID].value[j] = vec4(float(4*j), float(4*j) + 1.0, float(4*j) + 2.0, float(4*j) " "+ 3.0);\n" "\n" " for (int i = 0; i < (gl_MaxTessControlInputComponents-4)/4; i++)\n" " {\n" " outVariables[gl_InvocationID].value[j] += inVertex[gl_InvocationID].value[i];\n" " }\n" " }\n" "}\n"; /* Tessellation Control Shader code for max_in_out_attributes test */ const char* TessellationShaderTessellationMaxInOut::m_tcs_code_2 = "${VERSION}\n" "\n" "${TESSELLATION_SHADER_REQUIRE}\n" "\n" "precision highp float;\n" "\n" "layout(vertices = 2) out;\n" "\n" "patch out vec4 value[gl_MaxTessPatchComponents/4];\n" "\n" "void main()\n" "{\n" " gl_TessLevelInner[0] = 1.0;\n" " gl_TessLevelInner[1] = 1.0;\n" " gl_TessLevelOuter[0] = 1.0;\n" " gl_TessLevelOuter[1] = 1.0;\n" " gl_TessLevelOuter[2] = 1.0;\n" " gl_TessLevelOuter[3] = 1.0;\n" "\n" " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" "\n" " for (int j = 0; j < gl_MaxTessPatchComponents / 4; j++)\n" " {\n" " value[j] = vec4(float(4*j), float(4*j) + 1.0, float(4*j) + 2.0, float(4*j) + 3.0);\n" " }\n" "}\n"; /* Tessellation Evaluation Shader code for max_in_out_attributes test */ const char* TessellationShaderTessellationMaxInOut::m_tes_code_1 = "${VERSION}\n" "\n" "${TESSELLATION_SHADER_REQUIRE}\n" "\n" "precision highp float;\n" "\n" "layout (isolines, point_mode) in;\n" "\n" "in Vertex\n" "{\n" " vec4 value[(gl_MaxTessEvaluationInputComponents - 4) / 4];\n" "} inVariables[];\n" "\n" "out Vertex\n" "{\n" " vec4 value[(gl_MaxTessEvaluationOutputComponents - 4) / 4];\n" "} outVariables;\n" "\n" "void main()\n" "{\n" " gl_Position = gl_in[0].gl_Position;\n" "\n" " for (int j = 0; j < (gl_MaxTessEvaluationOutputComponents - 4) / 4; j++)\n" " {\n" " outVariables.value[j] = vec4(float(4*j), float(4*j) + 1.0, float(4*j) + 2.0, float(4*j) + 3.0);\n" "\n" " for (int i = 0 ; i < (gl_MaxTessEvaluationInputComponents - 4) / 4; i++)\n" " {\n" " outVariables.value[j] += inVariables[0].value[i];\n" " }\n" " }\n" "}\n"; /* Tessellation Evaluation Shader code for max_in_out_attributes test */ const char* TessellationShaderTessellationMaxInOut::m_tes_code_2 = "${VERSION}\n" "\n" "${TESSELLATION_SHADER_REQUIRE}\n" "\n" "precision highp float;\n" "\n" "layout (isolines, point_mode) in;\n" "\n" "patch in vec4 value[gl_MaxTessPatchComponents / 4];\n" "\n" "out vec4 out_value;\n" "\n" "void main()\n" "{\n" " gl_Position = gl_in[0].gl_Position;\n" " out_value = vec4(0.0);\n" "\n" " for (int i = 0; i < gl_MaxTessPatchComponents / 4; i++)\n" " {\n" " out_value += value[i];\n" " }\n" "}\n"; /* Fragment Shader code for max_in_out_attributes test */ const char* TessellationShaderTessellationMaxInOut::m_fs_code = "${VERSION}\n" "\n" "void main()\n" "{\n" "}\n"; /** Constructor * * @param context Test context **/ TessellationShaderTessellationTests::TessellationShaderTessellationTests(glcts::Context& context, const ExtParameters& extParams) : TestCaseGroupBase(context, extParams, "tessellation_shader_tessellation", "Verifies general tessellation functionality") { /* No implementation needed */ } /** * Initializes test groups for geometry shader tests **/ void TessellationShaderTessellationTests::init(void) { addChild( new glcts::TessellationShaderTessellationgl_InvocationID_PatchVerticesIn_PrimitiveID(m_context, m_extParams)); addChild( new glcts::TessellationShaderTessellationgl_TessCoord(m_context, m_extParams, TESSELLATION_TEST_TYPE_TCS_TES)); addChild(new glcts::TessellationShaderTessellationgl_TessCoord(m_context, m_extParams, TESSELLATION_TEST_TYPE_TES)); addChild(new glcts::TessellationShaderTessellationInputPatchDiscard(m_context, m_extParams)); addChild(new glcts::TessellationShaderTessellationMaxInOut(m_context, m_extParams)); } /** Constructor * * @param context Test context **/ TessellationShaderTessellationInputPatchDiscard::TessellationShaderTessellationInputPatchDiscard( Context& context, const ExtParameters& extParams) : TestCaseBase(context, extParams, "input_patch_discard", "Verifies that patches, for which relevant outer tessellation levels have" " been defined to 0 or less, are discard by the tessellation primitive " " generator.") , m_bo_id(0) , m_fs_id(0) , m_vs_id(0) , m_vao_id(0) , m_utils_ptr(0) { /* Left blank on purpose */ } /** Deinitializes ES objects created for the test. */ void TessellationShaderTessellationInputPatchDiscard::deinit() { /* Call base class' deinit() */ TestCaseBase::deinit(); if (!m_is_tessellation_shader_supported) { return; } const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Remove TF 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); /* Restore GL_PATCH_VERTICES_EXT value */ gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, 3); /* Unbind vertex array object */ gl.bindVertexArray(0); /* Free all ES objects we allocated for the test */ if (m_bo_id != 0) { gl.deleteBuffers(1, &m_bo_id); m_bo_id = 0; } if (m_fs_id != 0) { gl.deleteShader(m_fs_id); m_fs_id = 0; } if (m_vs_id != 0) { gl.deleteShader(m_vs_id); m_vs_id = 0; } if (m_vao_id != 0) { gl.deleteVertexArrays(1, &m_vao_id); m_vao_id = 0; } /* Deinitialize all test descriptors */ for (_runs::iterator it = m_runs.begin(); it != m_runs.end(); ++it) { deinitRun(*it); } m_runs.clear(); /* Release tessellation shader test utilities instance */ if (m_utils_ptr != NULL) { delete m_utils_ptr; m_utils_ptr = NULL; } } /** Deinitialize all test pass-specific ES objects. * * @param test Descriptor of a test pass to deinitialize. **/ void TessellationShaderTessellationInputPatchDiscard::deinitRun(_run& run) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); if (run.po_id != 0) { gl.deleteProgram(run.po_id); run.po_id = 0; } if (run.tc_id != 0) { gl.deleteShader(run.tc_id); run.tc_id = 0; } if (run.te_id != 0) { gl.deleteShader(run.te_id); run.te_id = 0; } } /** Returns source code of a tessellation control shader for the test. * * @return Requested string. **/ std::string TessellationShaderTessellationInputPatchDiscard::getTCCode() { std::string result; result = "${VERSION}\n" "\n" "${TESSELLATION_SHADER_REQUIRE}\n" "\n" "layout(vertices = 2) out;\n" "\n" "out int tc_primitive_id[];\n" "\n" "void main()\n" "{\n" " if ((gl_PrimitiveID % 4) == 0)\n" " {\n" " gl_TessLevelOuter[0] = 0.0;\n" " gl_TessLevelOuter[1] = 0.0;\n" " gl_TessLevelOuter[2] = 0.0;\n" " gl_TessLevelOuter[3] = 0.0;\n" " }\n" " else\n" " if ((gl_PrimitiveID % 4) == 2)\n" " {\n" " gl_TessLevelOuter[0] = -1.0;\n" " gl_TessLevelOuter[1] = -1.0;\n" " gl_TessLevelOuter[2] = -1.0;\n" " gl_TessLevelOuter[3] = -1.0;\n" " }\n" " else\n" " {\n" " gl_TessLevelOuter[0] = 1.0;\n" " gl_TessLevelOuter[1] = 1.0;\n" " gl_TessLevelOuter[2] = 1.0;\n" " gl_TessLevelOuter[3] = 1.0;\n" " }\n" "\n" " gl_TessLevelInner[0] = 1.0;\n" " gl_TessLevelInner[1] = 1.0;\n" " gl_out [gl_InvocationID].gl_Position = gl_in[0].gl_Position;\n" " tc_primitive_id [gl_InvocationID] = gl_PrimitiveID;\n" "}\n"; return result; } /** Returns source code of a tessellation evaluation shader for the test, * given user-specified vertex spacing and primitive modes. * * Throws TestError exception if either of the arguments is invalid. * * @param primitive_mode Primitive mode to use in the shader. * * @return Requested string. **/ std::string TessellationShaderTessellationInputPatchDiscard::getTECode(_tessellation_primitive_mode primitive_mode) { std::string result = "${VERSION}\n" "\n" "${TESSELLATION_SHADER_REQUIRE}\n" "\n" "layout(PRIMITIVE_MODE) in;\n" "\n" "in int tc_primitive_id [];\n" "out ivec2 te_tc_primitive_id;\n" "out int te_primitive_id;\n" "\n" "void main()\n" "{\n" " te_tc_primitive_id[0] = tc_primitive_id[0];\n" " te_tc_primitive_id[1] = tc_primitive_id[1];\n" " te_primitive_id = gl_PrimitiveID;\n" "}\n"; /* Replace PRIMITIVE_MODE token with actual primitive_mode */ std::string primitive_mode_string = TessellationShaderUtils::getESTokenForPrimitiveMode(primitive_mode); const char* primitive_mode_token = "PRIMITIVE_MODE"; std::size_t primitive_mode_token_index = std::string::npos; while ((primitive_mode_token_index = result.find(primitive_mode_token)) != std::string::npos) { result = result.replace(primitive_mode_token_index, strlen(primitive_mode_token), primitive_mode_string); primitive_mode_token_index = result.find(primitive_mode_token); } /* Done */ return result; } /** Initializes ES objects necessary to run the test. */ void TessellationShaderTessellationInputPatchDiscard::initTest() { /* Skip if required extensions are not supported. */ if (!m_is_tessellation_shader_supported) { throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED); } /* Generate all test-wide objects needed for test execution */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); m_utils_ptr = new TessellationShaderUtils(gl, this); gl.genVertexArrays(1, &m_vao_id); GLU_EXPECT_NO_ERROR(gl.getError(), "Could not generate vertex array object"); gl.bindVertexArray(m_vao_id); GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding vertex array object!"); gl.genBuffers(1, &m_bo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers() failed"); m_fs_id = gl.createShader(GL_FRAGMENT_SHADER); m_vs_id = gl.createShader(GL_VERTEX_SHADER); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() failed"); /* Configure fragment shader body */ const char* fs_body = "${VERSION}\n" "\n" "void main()\n" "{\n" "}\n"; shaderSourceSpecialized(m_fs_id, 1 /* count */, &fs_body); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for fragment shader"); /* Configure vertex shader body */ const char* vs_body = "${VERSION}\n" "\n" "void main()\n" "{\n" " gl_Position = vec4(1.0, 0.0, 0.0, 1.0);\n" "}\n"; shaderSourceSpecialized(m_vs_id, 1 /* count */, &vs_body); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for vertex shader"); /* Compile all the shaders */ const glw::GLuint shaders[] = { m_fs_id, m_vs_id }; const unsigned int n_shaders = sizeof(shaders) / sizeof(shaders[0]); m_utils_ptr->compileShaders(n_shaders, shaders, true); /* Initialize all the runs. */ const _tessellation_primitive_mode primitive_modes[] = { TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES, TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS, TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES }; const unsigned int n_primitive_modes = sizeof(primitive_modes) / sizeof(primitive_modes[0]); for (unsigned int n_primitive_mode = 0; n_primitive_mode < n_primitive_modes; ++n_primitive_mode) { /* Initialize the run */ _tessellation_primitive_mode primitive_mode = primitive_modes[n_primitive_mode]; _run run; initRun(run, primitive_mode); /* Store the run */ m_runs.push_back(run); } /* for (all primitive modes) */ /* Set up buffer object bindings. Storage size will be determined on * a per-iteration basis. **/ gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_bo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() failed"); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* index */, m_bo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase() failed"); } /** Initializes all ES objects necessary to run a specific test pass. * * Throws TestError exception if any of the arguments is found invalid. * * @param run Run descriptor to fill with IDs of initialized objects. * @param primitive_mode Primitive mode to use for the pass. **/ void TessellationShaderTessellationInputPatchDiscard::initRun(_run& run, _tessellation_primitive_mode primitive_mode) { run.primitive_mode = primitive_mode; /* Set up a program object for the descriptor */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); run.po_id = gl.createProgram(); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() failed"); /* Set up tessellation shader objects. */ run.tc_id = gl.createShader(m_glExtTokens.TESS_CONTROL_SHADER); run.te_id = gl.createShader(m_glExtTokens.TESS_EVALUATION_SHADER); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() call(s) failed"); /* Configure tessellation control shader body */ std::string tc_body = getTCCode(); const char* tc_body_raw_ptr = tc_body.c_str(); shaderSourceSpecialized(run.tc_id, 1 /* count */, &tc_body_raw_ptr); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for tessellation control shader"); /* Configure tessellation evaluation shader body */ std::string te_body = getTECode(primitive_mode); const char* te_body_raw_ptr = te_body.c_str(); shaderSourceSpecialized(run.te_id, 1 /* count */, &te_body_raw_ptr); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for tessellation evaluation shader"); /* Compile the tessellation evaluation shader */ glw::GLuint shaders[] = { run.tc_id, run.te_id }; const unsigned int n_shaders = sizeof(shaders) / sizeof(shaders[0]); m_utils_ptr->compileShaders(n_shaders, shaders, true); /* Attach all shader to the program object */ gl.attachShader(run.po_id, m_fs_id); gl.attachShader(run.po_id, run.tc_id); gl.attachShader(run.po_id, run.te_id); gl.attachShader(run.po_id, m_vs_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader() failed"); /* Set up XFB */ const char* varyings[] = { "te_tc_primitive_id", "te_primitive_id" }; const unsigned int n_varyings = sizeof(varyings) / sizeof(varyings[0]); gl.transformFeedbackVaryings(run.po_id, n_varyings, varyings, GL_INTERLEAVED_ATTRIBS); GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings() failed"); /* Link the program object */ glw::GLint link_status = GL_FALSE; gl.linkProgram(run.po_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glLinkProgram() failed"); gl.getProgramiv(run.po_id, GL_LINK_STATUS, &link_status); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv() failed"); if (link_status != GL_TRUE) { TCU_FAIL("Program linking failed"); } } /** Executes the test. * * Sets the test result to QP_TEST_RESULT_FAIL if the test failed, QP_TEST_RESULT_PASS otherwise. * * Note the function throws exception should an error occur! * * @return STOP if the test has finished, CONTINUE to indicate iterate() should be called once again. **/ tcu::TestNode::IterateResult TessellationShaderTessellationInputPatchDiscard::iterate(void) { /* Do not execute if required extensions are not supported. */ if (!m_is_tessellation_shader_supported) { throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED); } /* Initialize ES test objects */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); initTest(); /* We don't need rasterization for this test */ gl.enable(GL_RASTERIZER_DISCARD); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnable(GL_RASTERIZER_DISCARD) failed."); /* Configure amount of vertices per input patch */ gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, 1); GLU_EXPECT_NO_ERROR(gl.getError(), "glPatchParameteriEXT() failed for GL_PATCH_VERTICES_EXT pname"); /* Iterate through all tests configured */ for (_runs_const_iterator run_iterator = m_runs.begin(); run_iterator != m_runs.end(); run_iterator++) { const _run& run = *run_iterator; /* Set up XFB target BO storage size. Each input patch will generate: * * a) 1 ivec2 and 1 int IF it is an odd patch; * b) 0 bytes otherwise. * * This gives us a total of 2 * (sizeof(int)*2 + sizeof(int)) = 24 bytes per result coordinate, * assuming it does not get discarded along the way. * * Amount of vertices that TE processes is mode-dependent. Note that for 'quads' we use 6 instead * of 4 because the geometry will be broken down to triangles (1 quad = 2 triangles = 6 vertices) * later in the pipeline. */ const unsigned int n_total_primitives = 4; const unsigned int n_vertices_per_patch = ((run.primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES) ? 2 : (run.primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS) ? 6 : 3); const unsigned int n_bytes_per_result_vertex = sizeof(int) + 2 * sizeof(int); const unsigned int n_bytes_needed = (n_total_primitives / 2) * n_vertices_per_patch * n_bytes_per_result_vertex; gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, n_bytes_needed, NULL, /* data */ GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() failed"); /* Activate the program object */ gl.useProgram(run.po_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() failed"); /* Draw the test geometry. */ glw::GLenum tf_mode = TessellationShaderUtils::getTFModeForPrimitiveMode(run.primitive_mode, false); /* is_point_mode_enabled */ gl.beginTransformFeedback(tf_mode); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback() failed."); gl.drawArrays(m_glExtTokens.PATCHES, 0 /* first */, 1 /* vertices per patch */ * n_total_primitives); GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays() failed"); gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback() failed"); /* Map the BO with result data into user space */ int* result_data = (int*)gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, /* offset */ n_bytes_needed, GL_MAP_READ_BIT | GL_MAP_WRITE_BIT); GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange() call failed"); /* Verification is based on the following reasoning: * * a) Both TC and TE stages should operate on the same primitive IDs (no re-ordering * of the IDs is allowed) * b) Under test-specific configuration, tessellator will output 2 line segments (4 coordinates). * Two first two coordinates will be generated during tessellation of the second primitive, * and the other two coordinates will be generated during tessellation of the fourth primitive * (out of all four primitives that will enter the pipeline). * c) In case of quads, 6 first coordinates will be generated during tessellation of the second primitive, * and the other six will be generated during tessellation of the fourth primitive. * d) Finally, tessellator will output 2 triangles (6 coordinates). The first three coordinates will * be generated during tessellation of the second primitive, and the other two for the fourth * primitive. * */ const int expected_primitive_ids[] = { 1, /* second primitive */ 3 /* fourth primitive */ }; const unsigned int n_expected_primitive_ids = sizeof(expected_primitive_ids) / sizeof(expected_primitive_ids[0]); const unsigned int n_expected_repetitions = (run.primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES) ? 2 : (run.primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS) ? 6 : 3; const int* traveller_ptr = result_data; for (unsigned int n_primitive_id = 0; n_primitive_id < n_expected_primitive_ids; ++n_primitive_id) { int expected_primitive_id = expected_primitive_ids[n_primitive_id]; for (unsigned int n_repetition = 0; n_repetition < n_expected_repetitions; ++n_repetition) { for (unsigned int n_integer = 0; n_integer < 3 /* ivec2 + int */; ++n_integer) { if (*traveller_ptr != expected_primitive_id) { std::string primitive_mode_string = TessellationShaderUtils::getESTokenForPrimitiveMode(run.primitive_mode); m_testCtx.getLog() << tcu::TestLog::Message << "For primitive mode: " << primitive_mode_string << " invalid gl_PrimitiveID of value " << *traveller_ptr << " was found instead of expected " << expected_primitive_id << " at index:" << n_primitive_id * 3 /* ivec2 + int */ + n_integer << tcu::TestLog::EndMessage; TCU_FAIL("Discard mechanism failed"); } traveller_ptr++; } /* for (all captured integers) */ } /* for (all repetitions) */ } /* for (all non-discarded primitive ids) */ /* Clear the buffer storage space before we unmap it */ memset(result_data, 0, n_bytes_needed); /* Unmap the BO */ gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER); GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer() call failed"); } /* All done */ m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); return STOP; } /** Constructor * * @param context Test context **/ TessellationShaderTessellationgl_InvocationID_PatchVerticesIn_PrimitiveID:: TessellationShaderTessellationgl_InvocationID_PatchVerticesIn_PrimitiveID(Context& context, const ExtParameters& extParams) : TestCaseBase(context, extParams, "gl_InvocationID_PatchVerticesIn_PrimitiveID", "Verifies that gl_InvocationID, gl_PatchVerticesIn and gl_PrimitiveID " "are assigned correct values in TC and TE stages (where appropriate), " "when invoked with arrayed and indiced draw calls. Also verifies that " "restarting primitive topology does not restart primitive ID counter.") , m_bo_id(0) , m_fs_id(0) , m_vs_id(0) , m_vao_id(0) , m_utils_ptr(0) { /* Left blank on purpose */ } /** Deinitializes ES objects created for the test. */ void TessellationShaderTessellationgl_InvocationID_PatchVerticesIn_PrimitiveID::deinit() { /* Call base class' deinit() */ TestCaseBase::deinit(); if (!m_is_tessellation_shader_supported) { return; } const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Disable modes this test has enabled */ gl.disable(GL_RASTERIZER_DISCARD); GLU_EXPECT_NO_ERROR(gl.getError(), "glDisable(GL_RASTERIZER_DISCARD) failed"); gl.disable(GL_PRIMITIVE_RESTART_FIXED_INDEX); GLU_EXPECT_NO_ERROR(gl.getError(), "glDisable(GL_PRIMITIVE_RESTART_FIXED_INDEX) failed"); /* Remove TF buffer object bindings */ gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* buffer */); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* index */, 0 /* buffer */); /* Restore GL_PATCH_VERTICES_EXT value */ gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, 3); /* Unbind vertex array object */ gl.bindVertexArray(0); /* Free all ES objects we allocated for the test */ if (m_bo_id != 0) { gl.deleteBuffers(1, &m_bo_id); m_bo_id = 0; } if (m_fs_id != 0) { gl.deleteShader(m_fs_id); m_fs_id = 0; } if (m_vs_id != 0) { gl.deleteShader(m_vs_id); m_vs_id = 0; } if (m_vao_id != 0) { gl.deleteVertexArrays(1, &m_vao_id); m_vao_id = 0; } /* Deinitialize all run descriptors */ for (_runs::iterator it = m_runs.begin(); it != m_runs.end(); ++it) { deinitRun(*it); } m_runs.clear(); /* Release tessellation shader test utilities instance */ if (m_utils_ptr != NULL) { delete m_utils_ptr; m_utils_ptr = NULL; } } /** Deinitialize all test pass-specific ES objects. * * @param test Descriptor of a test pass to deinitialize. **/ void TessellationShaderTessellationgl_InvocationID_PatchVerticesIn_PrimitiveID::deinitRun(_run& run) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); if (run.bo_indices_id != 0) { gl.deleteBuffers(1, &run.bo_indices_id); run.bo_indices_id = 0; } if (run.po_id != 0) { gl.deleteProgram(run.po_id); run.po_id = 0; } if (run.tc_id != 0) { gl.deleteShader(run.tc_id); run.tc_id = 0; } if (run.te_id != 0) { gl.deleteShader(run.te_id); run.te_id = 0; } } /** Returns source code of a tessellation control shader. * * @param n_patch_vertices Amount of vertices per patch to * output in TC stage; * * @return Requested string. **/ std::string TessellationShaderTessellationgl_InvocationID_PatchVerticesIn_PrimitiveID::getTCCode( glw::GLuint n_patch_vertices) { static const char* tc_body = "${VERSION}\n" "\n" "${TESSELLATION_SHADER_REQUIRE}\n" "\n" "layout(vertices = N_PATCH_VERTICES) out;\n" "\n" "out TC_OUT\n" "{\n" " int tc_invocation_id;\n" " int tc_patch_vertices_in;\n" " int tc_primitive_id;\n" "} out_te[];\n" "\n" "void main()\n" "{\n" " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" " out_te[gl_InvocationID].tc_invocation_id = gl_InvocationID;\n" " out_te[gl_InvocationID].tc_patch_vertices_in = gl_PatchVerticesIn;\n" " out_te[gl_InvocationID].tc_primitive_id = gl_PrimitiveID;\n" "\n" " gl_TessLevelInner[0] = 4.0;\n" " gl_TessLevelInner[1] = 4.0;\n" " gl_TessLevelOuter[0] = 4.0;\n" " gl_TessLevelOuter[1] = 4.0;\n" " gl_TessLevelOuter[2] = 4.0;\n" " gl_TessLevelOuter[3] = 4.0;\n" "}\n"; /* Construct a string out of user-provided integer value */ std::stringstream n_patch_vertices_sstream; std::string n_patch_vertices_string; n_patch_vertices_sstream << n_patch_vertices; n_patch_vertices_string = n_patch_vertices_sstream.str(); /* Replace N_PATCH_VERTICES with user-provided value */ std::string result = tc_body; const std::string token = "N_PATCH_VERTICES"; std::size_t token_index = std::string::npos; while ((token_index = result.find(token)) != std::string::npos) { result = result.replace(token_index, token.length(), n_patch_vertices_string.c_str()); token_index = result.find(token); } return result; } /** Returns source code of a tessellation evaluation shader for the test, * given user-specified primitive mode. * * Throws TestError exception if either of the arguments is invalid. * * @param primitive_mode Primitive mode to use in the shader. * * @return Requested string. **/ std::string TessellationShaderTessellationgl_InvocationID_PatchVerticesIn_PrimitiveID::getTECode( _tessellation_primitive_mode primitive_mode) { static const char* te_body = "${VERSION}\n" "\n" "${TESSELLATION_SHADER_REQUIRE}\n" "\n" "layout(PRIMITIVE_MODE) in;\n" "\n" "in TC_OUT\n" "{\n" " int tc_invocation_id;\n" " int tc_patch_vertices_in;\n" " int tc_primitive_id;\n" "} in_tc[];\n" "\n" "out int te_tc_invocation_id;\n" "out int te_tc_patch_vertices_in;\n" "out int te_tc_primitive_id;\n" "out int te_patch_vertices_in;\n" "out int te_primitive_id;\n" "\n" "void main()\n" "{\n" " te_tc_invocation_id = in_tc[gl_PatchVerticesIn-1].tc_invocation_id;\n" " te_tc_patch_vertices_in = in_tc[gl_PatchVerticesIn-1].tc_patch_vertices_in;\n" " te_tc_primitive_id = in_tc[gl_PatchVerticesIn-1].tc_primitive_id;\n" " te_patch_vertices_in = gl_PatchVerticesIn;\n" " te_primitive_id = gl_PrimitiveID;\n" "}"; /* Replace PRIMITIVE_MODE with user-provided value */ std::string primitive_mode_string = TessellationShaderUtils::getESTokenForPrimitiveMode(primitive_mode); std::string result = te_body; const std::string token = "PRIMITIVE_MODE"; std::size_t token_index = std::string::npos; while ((token_index = result.find(token)) != std::string::npos) { result = result.replace(token_index, token.length(), primitive_mode_string.c_str()); token_index = result.find(token); } return result; } /** Initializes ES objects necessary to run the test. */ void TessellationShaderTessellationgl_InvocationID_PatchVerticesIn_PrimitiveID::initTest() { /* Skip if required extensions are not supported. */ if (!m_is_tessellation_shader_supported) { throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED); } /* Set up Utils instance */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); m_utils_ptr = new TessellationShaderUtils(gl, this); /* Initialize vertex array object */ gl.genVertexArrays(1, &m_vao_id); GLU_EXPECT_NO_ERROR(gl.getError(), "Could not generate vertex array object"); gl.bindVertexArray(m_vao_id); GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding vertex array object!"); /* Generate all test-wide objects needed for test execution */ gl.genBuffers(1, &m_bo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers() failed"); m_fs_id = gl.createShader(GL_FRAGMENT_SHADER); m_vs_id = gl.createShader(GL_VERTEX_SHADER); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() failed"); /* Configure fragment shader body */ const char* fs_body = "${VERSION}\n" "\n" "void main()\n" "{\n" "}\n"; shaderSourceSpecialized(m_fs_id, 1 /* count */, &fs_body); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for fragment shader"); /* Configure vertex shader body */ const char* vs_body = "${VERSION}\n" "\n" "in vec4 vertex_data;\n" "\n" "void main()\n" "{\n" " gl_Position = vertex_data;\n" "}\n"; shaderSourceSpecialized(m_vs_id, 1 /* count */, &vs_body); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for vertex shader"); /* Compile all the shaders */ const glw::GLuint shaders[] = { m_fs_id, m_vs_id }; const unsigned int n_shaders = sizeof(shaders) / sizeof(shaders[0]); m_utils_ptr->compileShaders(n_shaders, shaders, true /* should_succeed */); /* Retrieve GL_MAX_PATCH_VERTICES_EXT value before we continue */ glw::GLint gl_max_patch_vertices_value = 0; gl.getIntegerv(m_glExtTokens.MAX_PATCH_VERTICES, &gl_max_patch_vertices_value); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() failed for GL_MAX_PATCH_VERTICES_EXT pname"); /* Initialize all test passes */ const unsigned int drawcall_count_multipliers[] = { 3, 6 }; const bool is_indiced_draw_call_flags[] = { false, true }; const glw::GLint n_instances[] = { 1, 4 }; const glw::GLint n_patch_vertices[] = { 4, gl_max_patch_vertices_value / 2, gl_max_patch_vertices_value }; const _tessellation_primitive_mode primitive_modes[] = { TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES, TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS, TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES }; const unsigned int n_drawcall_count_multipliers = sizeof(drawcall_count_multipliers) / sizeof(drawcall_count_multipliers[0]); const unsigned int n_is_indiced_draw_call_flags = sizeof(is_indiced_draw_call_flags) / sizeof(is_indiced_draw_call_flags[0]); const unsigned int n_n_instances = sizeof(n_instances) / sizeof(n_instances[0]); const unsigned int n_n_patch_vertices = sizeof(n_patch_vertices) / sizeof(n_patch_vertices[0]); const unsigned int n_primitive_modes = sizeof(primitive_modes) / sizeof(primitive_modes[0]); for (unsigned int n_primitive_mode = 0; n_primitive_mode < n_primitive_modes; ++n_primitive_mode) { _tessellation_primitive_mode current_primitive_mode = primitive_modes[n_primitive_mode]; for (unsigned int n_patch_vertices_item = 0; n_patch_vertices_item < n_n_patch_vertices; ++n_patch_vertices_item) { glw::GLint current_n_patch_vertices = n_patch_vertices[n_patch_vertices_item]; for (unsigned int n_is_indiced_draw_call_flag = 0; n_is_indiced_draw_call_flag < n_is_indiced_draw_call_flags; ++n_is_indiced_draw_call_flag) { bool current_is_indiced_draw_call = is_indiced_draw_call_flags[n_is_indiced_draw_call_flag]; for (unsigned int n_instances_item = 0; n_instances_item < n_n_instances; ++n_instances_item) { glw::GLint current_n_instances = n_instances[n_instances_item]; for (unsigned int n_drawcall_count_multiplier = 0; n_drawcall_count_multiplier < n_drawcall_count_multipliers; ++n_drawcall_count_multiplier) { const unsigned int drawcall_count_multiplier = drawcall_count_multipliers[n_drawcall_count_multiplier]; /* Form the run descriptor */ _run run; initRun(run, current_primitive_mode, current_n_patch_vertices, current_is_indiced_draw_call, current_n_instances, drawcall_count_multiplier); /* Store the descriptor for later execution */ m_runs.push_back(run); } } /* for (all 'number of instances' settings) */ } /* for (all 'is indiced draw call' flags) */ } /* for (all 'n patch vertices' settings) */ } /* for (all primitive modes) */ /* Set up buffer object bindings. Storage size will be determined on * a per-iteration basis. **/ gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_bo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() failed"); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* index */, m_bo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase() failed"); } /** Initializes all ES objects necessary to run a specific test pass. * * Throws TestError exception if any of the arguments is found invalid. * * @param run Test run descriptor to fill with IDs of initialized objects. * @param primitive_mode Primitive mode to use for the pass. * @param n_patch_vertices Amount of output patch vertices to use for the pass. * @param is_indiced true if the draw call to be used for the test run should be indiced; * false to use the non-indiced one. * @param n_instances Amount of instances to use for the draw call. Set to 1 if the draw * call should be non-instanced. * @param drawcall_count_multiplier Will be used to multiply the "count" argument of the draw call API * function, effectively multiplying amount of primitives that will be * generated by the tessellator. **/ void TessellationShaderTessellationgl_InvocationID_PatchVerticesIn_PrimitiveID::initRun( _run& run, _tessellation_primitive_mode primitive_mode, glw::GLint n_patch_vertices, bool is_indiced, glw::GLint n_instances, unsigned int drawcall_count_multiplier) { run.drawcall_count_multiplier = drawcall_count_multiplier; run.drawcall_is_indiced = is_indiced; run.n_instances = n_instances; run.n_patch_vertices = n_patch_vertices; run.primitive_mode = primitive_mode; /* Set up a program object for the descriptor */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); run.po_id = gl.createProgram(); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() failed"); /* Set up tessellation control shader object */ std::string tc_body = getTCCode(n_patch_vertices); const char* tc_body_raw_ptr = tc_body.c_str(); run.tc_id = gl.createShader(m_glExtTokens.TESS_CONTROL_SHADER); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() call failed"); shaderSourceSpecialized(run.tc_id, 1 /* count */, &tc_body_raw_ptr); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for tessellation control shader"); /* Set up tessellation evaluation shader object. */ std::string te_body = getTECode(primitive_mode); const char* te_body_raw_ptr = te_body.c_str(); run.te_id = gl.createShader(m_glExtTokens.TESS_EVALUATION_SHADER); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() call failed"); shaderSourceSpecialized(run.te_id, 1 /* count */, &te_body_raw_ptr); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for tessellation evaluation shader"); /* Compile the shaders */ const glw::GLuint shader_ids[] = { run.tc_id, run.te_id }; const unsigned int n_shader_ids = sizeof(shader_ids) / sizeof(shader_ids[0]); m_utils_ptr->compileShaders(n_shader_ids, shader_ids, true /* should_succeed */); /* Attach all shader to the program object */ gl.attachShader(run.po_id, m_fs_id); gl.attachShader(run.po_id, run.te_id); gl.attachShader(run.po_id, m_vs_id); gl.attachShader(run.po_id, run.tc_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader() failed"); /* Set up XFB */ const char* varyings[] = { "te_tc_invocation_id", "te_tc_patch_vertices_in", "te_tc_primitive_id", "te_patch_vertices_in", "te_primitive_id" }; const unsigned int n_varyings = sizeof(varyings) / sizeof(varyings[0]); gl.transformFeedbackVaryings(run.po_id, n_varyings, varyings, GL_INTERLEAVED_ATTRIBS); GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings() failed"); /* Link the program object */ glw::GLint link_status = GL_FALSE; gl.linkProgram(run.po_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glLinkProgram() failed"); gl.getProgramiv(run.po_id, GL_LINK_STATUS, &link_status); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv() failed"); if (link_status != GL_TRUE) { TCU_FAIL("Program linking failed"); } /* If this is going to be an indiced draw call, we need to initialize a buffer * object that will hold index data. GL_UNSIGNED_BYTE index type will be always * used for the purpose of this test. */ if (is_indiced) { gl.genBuffers(1, &run.bo_indices_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers() failed"); /* Implementations are allowed NOT to support primitive restarting for patches. * Take this into account and do not insert restart indices, if ES reports no * support. */ glw::GLboolean is_primitive_restart_supported = GL_TRUE; gl.getBooleanv(GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED, &is_primitive_restart_supported); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() failed for GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED pname"); /* Set up index buffer storage. Note that we're not using any attributes * in any stage - our goal is just to make sure the primitive counter does * not restart whenever restart index is encountered during a draw call */ DE_ASSERT(run.n_patch_vertices > 3); DE_ASSERT(run.drawcall_count_multiplier > 1); const unsigned int interleave_rate = run.n_patch_vertices * 2; unsigned char* bo_contents = DE_NULL; unsigned int bo_size = static_cast(sizeof(unsigned char) * run.n_patch_vertices * run.drawcall_count_multiplier); /* Count in restart indices if necessary */ if (is_primitive_restart_supported) { run.n_restart_indices = (bo_size / interleave_rate); bo_size += 1 /* restart index */ * run.n_restart_indices; } /* Allocate space for the index buffer */ bo_contents = new unsigned char[bo_size]; /* Interleave the restart index every two complete sets of vertices, each set * making up a full set of vertices. Fill all other indices with zeros. The * indices don't really matter since test shaders do not use any attributes - * what we want to verify is that the restart index does not break the primitive * id counter. * * NOTE: Our interleave rate is just an arbitrary value that makes * sense, given the multipliers we use for the test */ const unsigned char restart_index = 0xFF; memset(bo_contents, 0, bo_size); if (is_primitive_restart_supported) { for (unsigned int n_index = interleave_rate; n_index < bo_size; n_index += interleave_rate) { bo_contents[n_index] = restart_index; /* Move one index ahead */ n_index++; } } /* Set up the buffer object storage */ gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, run.bo_indices_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() failed"); gl.bufferData(GL_ELEMENT_ARRAY_BUFFER, bo_size, bo_contents, GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() failed"); /* Release the buffer */ delete[] bo_contents; bo_contents = NULL; } /* Retrieve amount of tessellation coordinates. * * Note: this test assumes a constant tessellation level value of 4 for all * inner/outer tessellation levels */ const glw::GLfloat tess_levels[] = { 4.0f, 4.0f, 4.0f, 4.0f }; run.n_result_vertices = m_utils_ptr->getAmountOfVerticesGeneratedByTessellator( run.primitive_mode, tess_levels, tess_levels, TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, false); /* is_point_mode_enabled */ /* The value we have at this point is for single patch only. To end up * with actual amount of coordinates that will be generated by the tessellator, * we need to multiply it by drawcall_count_multiplier * n_instances */ run.n_result_vertices *= run.drawcall_count_multiplier * run.n_instances; /* We're done! */ } /** 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 TessellationShaderTessellationgl_InvocationID_PatchVerticesIn_PrimitiveID::iterate(void) { /* Do not execute if required extensions are not supported. */ if (!m_is_tessellation_shader_supported) { throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED); } /* Initialize ES test objects */ initTest(); /* Initialize tessellation shader utilities */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* We don't need rasterization for this test */ gl.enable(GL_RASTERIZER_DISCARD); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnable(GL_RASTERIZER_DISCARD) failed."); /* Enable GL_PRIMITIVE_RESTART_FIXED_INDEX mode for indiced draw calls. */ gl.enable(GL_PRIMITIVE_RESTART_FIXED_INDEX); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX) failed."); /* Iterate through all test runs configured */ for (_runs_const_iterator run_iterator = m_runs.begin(); run_iterator != m_runs.end(); run_iterator++) { const _run& run = *run_iterator; /* Configure run-specific amount of vertices per patch */ gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, run.n_patch_vertices); GLU_EXPECT_NO_ERROR(gl.getError(), "glPatchParameteriEXT() failed for GL_PATCH_VERTICES_EXT pname"); /* Activate run-specific program object */ gl.useProgram(run.po_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() failed"); /* Update GL_ELEMENT_ARRAY_BUFFER binding, depending on run properties */ gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, (run.drawcall_is_indiced) ? run.bo_indices_id : 0); GLU_EXPECT_NO_ERROR(gl.getError(), "Could not update GL_ELEMENT_ARRAY_BUFFER binding"); /* Update transform feedback buffer bindings */ gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_bo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() failed"); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* index */, m_bo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase() failed"); /* Update the transform feedback buffer object storage. For each generated * tessellated coordinate, TE stage will output 5 integers. */ glw::GLint bo_size = static_cast(run.n_result_vertices * 5 /* ints */ * sizeof(int)); gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, bo_size, DE_NULL, GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() failed"); /* Render the geometry */ glw::GLint drawcall_count = run.n_patch_vertices * run.drawcall_count_multiplier + run.n_restart_indices; glw::GLenum tf_mode = TessellationShaderUtils::getTFModeForPrimitiveMode(run.primitive_mode, false); /* is_point_mode_enabled */ gl.beginTransformFeedback(tf_mode); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback() failed"); { if (run.drawcall_is_indiced) { if (run.n_instances != 1) { gl.drawElementsInstanced(m_glExtTokens.PATCHES, drawcall_count, GL_UNSIGNED_BYTE, DE_NULL, /* indices */ run.n_instances); GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawElementsInstanced() failed"); } /* if (run.n_instances != 0) */ else { gl.drawElements(m_glExtTokens.PATCHES, drawcall_count, GL_UNSIGNED_BYTE, DE_NULL); /* indices */ GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawElements() failed"); } } /* if (run.drawcall_is_indiced) */ else { if (run.n_instances != 1) { gl.drawArraysInstanced(m_glExtTokens.PATCHES, 0, /* first */ drawcall_count, run.n_instances); GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArraysInstanced() failed"); } else { gl.drawArrays(m_glExtTokens.PATCHES, 0, /* first */ drawcall_count); GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays() failed"); } } } gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback() failed"); /* Map the result buffer object */ const int* result_data = (const int*)gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, /* offset */ bo_size, GL_MAP_READ_BIT); GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange() failed"); /* Verify gl_InvocationID values used in both stages were correct. In TE: * * te_tc_invocation_id = in_tc[gl_PatchVerticesIn-1].tc_invocation_id; * * In the list of varyings passed to glTransformFeedbackVaryings(), * te_tc_invocation_id is the very first item, so no need to offset * result_data when initializing result_traveller_ptr below. */ const unsigned int n_int_varyings_per_tess_coordinate = 5; const int* result_traveller_ptr = result_data; for (unsigned int n_coordinate = 0; n_coordinate < run.n_result_vertices; ++n_coordinate, result_traveller_ptr += n_int_varyings_per_tess_coordinate) { const int expected_invocation_id = run.n_patch_vertices - 1; const int invocation_id_value = *result_traveller_ptr; if (invocation_id_value != expected_invocation_id) { m_testCtx.getLog() << tcu::TestLog::Message << "Invalid gl_InvocationID value (" << invocation_id_value << ") " "was found for result coordinate at index " << n_coordinate << " " "instead of expected value (" << expected_invocation_id << ")." << tcu::TestLog::EndMessage; TCU_FAIL("Invalid gl_InvocationID value used in TC stage"); } } /* for (all result coordinates) */ /* Verify gl_PrimitiveID values used in both stages were correct. In TE: * * te_tc_primitive_id = in_tc[gl_PatchVerticesIn-1].tc_primitive_id; * te_primitive_id = gl_PrimitiveID; * * In the list of varyings passed to glTransformFeedbackVaryings(), * te_tc_primitive_id is passed as 3rd string and te_primitive_id is located on * 5th location. */ const unsigned int n_result_vertices_per_patch_vertex_batch = run.n_result_vertices / run.drawcall_count_multiplier / run.n_instances; const unsigned int n_result_vertices_per_instance = run.n_result_vertices / run.n_instances; for (unsigned int n_coordinate = 0; n_coordinate < run.n_result_vertices; ++n_coordinate) { unsigned int actual_n_coordinate = n_coordinate; /* Subsequent instances reset gl_PrimitiveID counter */ while (actual_n_coordinate >= n_result_vertices_per_instance) { actual_n_coordinate -= n_result_vertices_per_instance; } /* Calculate expected gl_PrimitiveID value */ const int expected_primitive_id = actual_n_coordinate / n_result_vertices_per_patch_vertex_batch; /* te_tc_primitive_id */ result_traveller_ptr = result_data + n_coordinate * n_int_varyings_per_tess_coordinate + 2; /* as per comment */ if (*result_traveller_ptr != expected_primitive_id) { m_testCtx.getLog() << tcu::TestLog::Message << "Invalid gl_PrimitiveID value (" << *result_traveller_ptr << ") " "was used in TC stage instead of expected value (" << expected_primitive_id << ") " " as stored for result coordinate at index " << n_coordinate << "." << tcu::TestLog::EndMessage; TCU_FAIL("Invalid gl_PrimitiveID value used in TC stage"); } /* te_primitive_id */ result_traveller_ptr = result_data + n_coordinate * n_int_varyings_per_tess_coordinate + 4; /* as per comment */ if (*result_traveller_ptr != expected_primitive_id) { m_testCtx.getLog() << tcu::TestLog::Message << "Invalid gl_PrimitiveID value (" << *result_traveller_ptr << ") " "was used in TE stage instead of expected value (" << expected_primitive_id << ") " " as stored for result coordinate at index " << n_coordinate << "." << tcu::TestLog::EndMessage; TCU_FAIL("Invalid gl_PrimitiveID value used in TE stage"); } } /* for (all result coordinates) */ /* Verify gl_PatchVerticesIn values used in both stages were correct. In TE: * * te_tc_patch_vertices_in = in_tc[gl_PatchVerticesIn-1].tc_patch_vertices_in; * te_patch_vertices_in = gl_PatchVerticesIn; * * In the list of varyings passed to glTransformFeedbackVaryings(), * te_tc_patch_vertices_in takes 2nd location and te_patch_vertices_in is * located at 4th position. * **/ for (unsigned int n_coordinate = 0; n_coordinate < run.n_result_vertices; ++n_coordinate) { const int expected_patch_vertices_in_value = run.n_patch_vertices; /* te_tc_patch_vertices_in */ result_traveller_ptr = result_data + n_coordinate * n_int_varyings_per_tess_coordinate + 1; /* as per comment */ if (*result_traveller_ptr != expected_patch_vertices_in_value) { m_testCtx.getLog() << tcu::TestLog::Message << "Invalid gl_PatchVerticesIn value (" << *result_traveller_ptr << ") " "was used in TC stage instead of expected value (" << expected_patch_vertices_in_value << ") " " as stored for result coordinate at index " << n_coordinate << "." << tcu::TestLog::EndMessage; TCU_FAIL("Invalid gl_PatchVerticesIn value used in TC stage"); } /* te_patch_vertices_in */ result_traveller_ptr = result_data + n_coordinate * n_int_varyings_per_tess_coordinate + 3; /* as per comment */ if (*result_traveller_ptr != expected_patch_vertices_in_value) { m_testCtx.getLog() << tcu::TestLog::Message << "Invalid gl_PatchVerticesIn value (" << *result_traveller_ptr << ") " "was used in TE stage instead of expected value (" << expected_patch_vertices_in_value << ") " " as stored for result coordinate at index " << n_coordinate << "." << tcu::TestLog::EndMessage; } } /* for (all result coordinates) */ /* Unmap the buffer object - we're done with this iteration */ gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER); GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer() failed"); } /* for (all runs) */ /* All done */ m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); return STOP; } std::string TessellationShaderTessellationgl_TessCoord::getTypeName(_tessellation_test_type test_type) { static const char* names[2] = { "TCS_TES", "TES" }; DE_ASSERT(0 <= test_type && test_type <= DE_LENGTH_OF_ARRAY(names)); DE_STATIC_ASSERT(0 == TESSELLATION_TEST_TYPE_TCS_TES && 1 == TESSELLATION_TEST_TYPE_TES); return names[test_type]; } /** Constructor * * @param context Test context **/ TessellationShaderTessellationgl_TessCoord::TessellationShaderTessellationgl_TessCoord( Context& context, const ExtParameters& extParams, _tessellation_test_type test_type) : TestCaseBase(context, extParams, getTypeName(test_type).c_str(), "Verifies that u, v, w components of gl_TessCoord are within " "range for a variety of input/outer tessellation level combinations " "for all primitive modes. Verifies each component is within valid " " range. Also checks that w is always equal to 0 for isolines mode.") , m_test_type(test_type) , m_bo_id(0) , m_broken_ts_id(0) , m_fs_id(0) , m_vs_id(0) , m_vao_id(0) , m_utils_ptr(0) { /* Left blank on purpose */ } /** Deinitializes ES objects created for the test. */ void TessellationShaderTessellationgl_TessCoord::deinit() { /* Call base class' deinit() */ TestCaseBase::deinit(); if (!m_is_tessellation_shader_supported) { return; } if (glu::isContextTypeES(m_context.getRenderContext().getType()) && m_test_type == TESSELLATION_TEST_TYPE_TES) { 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); /* Restore GL_PATCH_VERTICES_EXT, GL_PATCH_DEFAULT_INNER_LEVEL and * GL_PATCH_DEFAULT_OUTER_LEVEL values */ if (!glu::isContextTypeES(m_context.getRenderContext().getType())) { const float default_tess_levels[] = { 1.0f, 1.0f, 1.0f, 1.0f }; gl.patchParameterfv(GL_PATCH_DEFAULT_INNER_LEVEL, default_tess_levels); gl.patchParameterfv(GL_PATCH_DEFAULT_OUTER_LEVEL, default_tess_levels); } gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, 3); /* Unbind vertex array object */ gl.bindVertexArray(0); /* Free all ES objects we allocated for the test */ if (m_bo_id != 0) { gl.deleteBuffers(1, &m_bo_id); m_bo_id = 0; } if (m_broken_ts_id != 0) { gl.deleteShader(m_broken_ts_id); m_broken_ts_id = 0; } if (m_fs_id != 0) { gl.deleteShader(m_fs_id); m_fs_id = 0; } if (m_vs_id != 0) { gl.deleteShader(m_vs_id); m_vs_id = 0; } if (m_vao_id != 0) { gl.deleteVertexArrays(1, &m_vao_id); m_vao_id = 0; } /* Deinitialize all test descriptors */ for (_tests::iterator it = m_tests.begin(); it != m_tests.end(); ++it) { deinitTestDescriptor(*it); } m_tests.clear(); /* Release tessellation shader test utilities instance */ if (m_utils_ptr != NULL) { delete m_utils_ptr; m_utils_ptr = NULL; } } /** Deinitialize all test pass-specific ES objects. * * @param test Descriptor of a test pass to deinitialize. **/ void TessellationShaderTessellationgl_TessCoord::deinitTestDescriptor(_test_descriptor& test) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); if (test.po_id != 0) { gl.deleteProgram(test.po_id); test.po_id = 0; } if (test.tc_id != 0) { gl.deleteShader(test.tc_id); test.tc_id = 0; } if (test.te_id != 0) { gl.deleteShader(test.te_id); test.te_id = 0; } } /** Returns source code of a tessellation control shader for the test, * given user-specified amount of output patch vertices. * * @param n_patch_vertices Amount of output patch vertices for TC stage. * * @return Requested string. **/ std::string TessellationShaderTessellationgl_TessCoord::getTCCode(glw::GLint n_patch_vertices) { return TessellationShaderUtils::getGenericTCCode(n_patch_vertices, true); } /** Returns source code of a tessellation evaluation shader for the test, * given user-specified vertex spacing and primitive modes. * * Throws TestError exception if either of the arguments is invalid. * * @param vertex_spacing Vertex spacing mode to use in the shader. * @param primitive_mode Primitive mode to use in the shader. * * @return Requested string. **/ std::string TessellationShaderTessellationgl_TessCoord::getTECode(_tessellation_shader_vertex_spacing vertex_spacing, _tessellation_primitive_mode primitive_mode) { return TessellationShaderUtils::getGenericTECode(vertex_spacing, primitive_mode, TESSELLATION_SHADER_VERTEX_ORDERING_CCW, false); } /** Initializes ES objects necessary to run the test. */ void TessellationShaderTessellationgl_TessCoord::initTest() { /* Skip if required extensions are not supported. */ if (!m_is_tessellation_shader_supported) { throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED); } if (glu::isContextTypeES(m_context.getRenderContext().getType()) && m_test_type == TESSELLATION_TEST_TYPE_TES) { throw tcu::NotSupportedError("Test can't be run in ES context"); } /* Generate all test-wide objects needed for test execution */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.genVertexArrays(1, &m_vao_id); GLU_EXPECT_NO_ERROR(gl.getError(), "Could not generate vertex array object"); gl.bindVertexArray(m_vao_id); GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding vertex array object!"); gl.genBuffers(1, &m_bo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers() failed"); m_broken_ts_id = gl.createShader(m_glExtTokens.TESS_EVALUATION_SHADER); m_fs_id = gl.createShader(GL_FRAGMENT_SHADER); m_vs_id = gl.createShader(GL_VERTEX_SHADER); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() failed"); /* Configure fragment shader body */ const char* fs_body = "${VERSION}\n" "\n" "void main()\n" "{\n" "}\n"; shaderSourceSpecialized(m_fs_id, 1 /* count */, &fs_body); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for fragment shader"); /* Configure vertex shader body */ const char* vs_body = "${VERSION}\n" "\n" "void main()\n" "{\n" " gl_Position = vec4(1.0, 0.0, 0.0, 1.0);\n" "}\n"; shaderSourceSpecialized(m_vs_id, 1 /* count */, &vs_body); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for vertex shader"); /* Compile all the shaders */ const glw::GLuint shaders[] = { m_fs_id, m_vs_id }; const unsigned int n_shaders = sizeof(shaders) / sizeof(shaders[0]); for (unsigned int n_shader = 0; n_shader < n_shaders; ++n_shader) { glw::GLuint shader = shaders[n_shader]; if (shader != 0) { glw::GLint compile_status = GL_FALSE; gl.compileShader(shader); GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader() failed"); gl.getShaderiv(shader, GL_COMPILE_STATUS, &compile_status); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv() failed"); if (compile_status != GL_TRUE) { TCU_FAIL("Shader compilation failed"); } } } /* for (all shaders) */ /* Retrieve GL_MAX_TESS_GEN_LEVEL_EXT value before we start looping */ glw::GLint gl_max_tess_gen_level_value = 0; gl.getIntegerv(m_glExtTokens.MAX_TESS_GEN_LEVEL, &gl_max_tess_gen_level_value); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() failed for GL_MAX_TESS_GEN_LEVEL_EXT pname"); /* Initialize all test passes - iterate over all primitive modes supported.. */ for (int primitive_mode = static_cast(TESSELLATION_SHADER_PRIMITIVE_MODE_FIRST); primitive_mode != static_cast(TESSELLATION_SHADER_PRIMITIVE_MODE_COUNT); primitive_mode++) { /* Iterate over all tessellation level combinations defined for current primitive mode */ _tessellation_levels_set tessellation_levels = TessellationShaderUtils::getTessellationLevelSetForPrimitiveMode( static_cast<_tessellation_primitive_mode>(primitive_mode), gl_max_tess_gen_level_value, TESSELLATION_LEVEL_SET_FILTER_ALL_LEVELS_USE_THE_SAME_VALUE); for (_tessellation_levels_set_const_iterator levels_iterator = tessellation_levels.begin(); levels_iterator != tessellation_levels.end(); levels_iterator++) { const _tessellation_levels& levels = *levels_iterator; _test_descriptor test; initTestDescriptor(test, TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, static_cast<_tessellation_primitive_mode>(primitive_mode), 1, /* n_patch_vertices */ levels.inner, levels.outer, m_test_type); /* Store the test descriptor */ m_tests.push_back(test); } /* for (all tessellation level combinations for current primitive mode) */ } /* for (all primitive modes) */ /* Set up buffer object bindings. Storage size will be determined on * a per-iteration basis. **/ gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_bo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() failed"); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* index */, m_bo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase() failed"); } /** Initializes all ES objects necessary to run a specific test pass. * * Throws TestError exception if any of the arguments is found invalid. * * @param test Test descriptor to fill with IDs of initialized objects. * @param vertex_spacing Vertex spacing mode to use for the pass. * @param primitive_mode Primitive mode to use for the pass. * @param n_patch_vertices Amount of output patch vertices to use for the pass. * @param inner_tess_level Inner tessellation level values to be used for the pass. * Must not be NULL. * @param outer_tess_level Outer tessellation level values to be used for the pass. * Must not be NULL. * @param test_type Defines which tessellation stages should be defined for the pass. **/ void TessellationShaderTessellationgl_TessCoord::initTestDescriptor( _test_descriptor& test, _tessellation_shader_vertex_spacing vertex_spacing, _tessellation_primitive_mode primitive_mode, glw::GLint n_patch_vertices, const float* inner_tess_level, const float* outer_tess_level, _tessellation_test_type test_type) { test.n_patch_vertices = n_patch_vertices; test.primitive_mode = primitive_mode; test.type = test_type; test.vertex_spacing = vertex_spacing; memcpy(test.tess_level_inner, inner_tess_level, sizeof(float) * 2 /* components */); memcpy(test.tess_level_outer, outer_tess_level, sizeof(float) * 4 /* components */); /* Set up a program object for the descriptor */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); test.po_id = gl.createProgram(); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() failed"); /* Set up a pass-specific tessellation shader objects. */ if (test_type == TESSELLATION_TEST_TYPE_TCS_TES) { test.tc_id = gl.createShader(m_glExtTokens.TESS_CONTROL_SHADER); } test.te_id = gl.createShader(m_glExtTokens.TESS_EVALUATION_SHADER); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() call(s) failed"); /* Configure tessellation control shader body */ if (test.tc_id != 0) { std::string tc_body = getTCCode(n_patch_vertices); const char* tc_body_raw_ptr = tc_body.c_str(); shaderSourceSpecialized(test.tc_id, 1 /* count */, &tc_body_raw_ptr); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for tessellation control shader"); } /* Configure tessellation evaluation shader body */ std::string te_body = getTECode(vertex_spacing, primitive_mode); const char* te_body_raw_ptr = te_body.c_str(); shaderSourceSpecialized(test.te_id, 1 /* count */, &te_body_raw_ptr); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for tessellation evaluation shader"); /* Compile the tessellation evaluation shader */ glw::GLint compile_status = GL_FALSE; glw::GLuint shaders[] = { test.tc_id, test.te_id }; const unsigned int n_shaders = sizeof(shaders) / sizeof(shaders[0]); for (unsigned int n_shader = 0; n_shader < n_shaders; ++n_shader) { glw::GLuint shader = shaders[n_shader]; if (shader != 0) { gl.compileShader(shader); GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader() failed for tessellation shader"); gl.getShaderiv(shader, GL_COMPILE_STATUS, &compile_status); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv() failed for tessellation shader"); if (compile_status != GL_TRUE) { TCU_FAIL("Tessellation shader compilation failed"); } } } /* for (all shaders) */ /* Attach all shader to the program object */ gl.attachShader(test.po_id, m_fs_id); gl.attachShader(test.po_id, test.te_id); gl.attachShader(test.po_id, m_vs_id); if (test.tc_id != 0) { gl.attachShader(test.po_id, test.tc_id); } GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader() failed"); /* Set up XFB */ const char* varyings[] = { "result_uvw" }; const unsigned int n_varyings = sizeof(varyings) / sizeof(varyings[0]); gl.transformFeedbackVaryings(test.po_id, n_varyings, varyings, GL_INTERLEAVED_ATTRIBS); GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings() failed"); /* Link the program object */ glw::GLint link_status = GL_FALSE; gl.linkProgram(test.po_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glLinkProgram() failed"); gl.getProgramiv(test.po_id, GL_LINK_STATUS, &link_status); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv() failed"); if (link_status != GL_TRUE) { TCU_FAIL("Program linking failed"); } /* If TCS stage is present, set up the corresponding uniforms as needed */ if (test.type == TESSELLATION_TEST_TYPE_TCS_TES) { test.inner_tess_level_uniform_location = gl.getUniformLocation(test.po_id, "inner_tess_level"); test.outer_tess_level_uniform_location = gl.getUniformLocation(test.po_id, "outer_tess_level"); DE_ASSERT(test.inner_tess_level_uniform_location != -1); DE_ASSERT(test.outer_tess_level_uniform_location != -1); /* Now that we have the locations, let's configure the uniforms */ gl.useProgram(test.po_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() call failed"); gl.uniform2fv(test.inner_tess_level_uniform_location, 1, /* count */ test.tess_level_inner); GLU_EXPECT_NO_ERROR(gl.getError(), "glUniform2fv() call failed"); gl.uniform4fv(test.outer_tess_level_uniform_location, 1, /* count */ test.tess_level_outer); GLU_EXPECT_NO_ERROR(gl.getError(), "glUniform4fv() call failed"); } } /** Executes the test. * * Sets the test result to QP_TEST_RESULT_FAIL if the test failed, QP_TEST_RESULT_PASS otherwise. * * Note the function throws exception should an error occur! * * @return STOP if the test has finished, CONTINUE to indicate iterate() should be called once again. **/ tcu::TestNode::IterateResult TessellationShaderTessellationgl_TessCoord::iterate(void) { /* Do not execute if required extensions are not supported. */ if (!m_is_geometry_shader_extension_supported || !m_is_tessellation_shader_supported) { throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED); } /* On ES skip test configurations that don't have TCS. */ if (isContextTypeES(m_context.getRenderContext().getType()) && (m_test_type == TESSELLATION_TEST_TYPE_TES)) { throw tcu::NotSupportedError("Implementation requires TCS and TES be used together; skipping."); } /* Initialize ES test objects */ initTest(); /* Initialize tessellation shader utilities */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); m_utils_ptr = new TessellationShaderUtils(gl, this); /* We don't need rasterization for this test */ gl.enable(GL_RASTERIZER_DISCARD); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnable(GL_RASTERIZER_DISCARD) failed."); /* Iterate through all tests configured */ for (_tests_const_iterator test_iterator = m_tests.begin(); test_iterator != m_tests.end(); test_iterator++) { const _test_descriptor& test = *test_iterator; if (!glu::isContextTypeES(m_context.getRenderContext().getType())) { /* If there is no TCS defined, define inner/outer tessellation levels */ if (test.type == TESSELLATION_TEST_TYPE_TES) { gl.patchParameterfv(GL_PATCH_DEFAULT_INNER_LEVEL, test.tess_level_inner); GLU_EXPECT_NO_ERROR(gl.getError(), "glPatchParameterfv() failed for GL_PATCH_DEFAULT_INNER_LEVEL pname"); gl.patchParameterfv(GL_PATCH_DEFAULT_OUTER_LEVEL, test.tess_level_outer); GLU_EXPECT_NO_ERROR(gl.getError(), "glPatchParameterfv() failed for GL_PATCH_DEFAULT_OUTER_LEVEL pname"); } } /* Configure amount of vertices per patch */ gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, test.n_patch_vertices); GLU_EXPECT_NO_ERROR(gl.getError(), "glPatchParameteriEXT() failed for GL_PATCH_VERTICES_EXT pname"); /* Set up XFB target BO storage size. We will be capturing a total of 12 FP components per * result vertex. */ unsigned int n_bytes_needed = 0; unsigned int n_result_vertices = 0; n_result_vertices = m_utils_ptr->getAmountOfVerticesGeneratedByTessellator( test.primitive_mode, test.tess_level_inner, test.tess_level_outer, test.vertex_spacing, false); n_bytes_needed = static_cast(n_result_vertices * sizeof(float) * 12 /* components */); gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, n_bytes_needed, NULL, /* data */ GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() failed"); /* Activate the program object */ gl.useProgram(test.po_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() failed"); /* Draw the test geometry. */ glw::GLenum tf_mode = TessellationShaderUtils::getTFModeForPrimitiveMode(test.primitive_mode, false); gl.beginTransformFeedback(tf_mode); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback(GL_PATCHES_EXT) failed."); gl.drawArrays(m_glExtTokens.PATCHES, 0 /* first */, test.n_patch_vertices); GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays() failed"); gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback() failed"); /* Map the BO with result data into user space */ const float* vertex_data = (const float*)gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, /* offset */ n_bytes_needed, GL_MAP_READ_BIT); GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange() call failed"); /* (test 1): Make sure that u+v+w == 1 (applicable for triangles only) */ const float epsilon = 1e-5f; if (test.primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES) { for (unsigned int n_vertex = 0; n_vertex < n_result_vertices; ++n_vertex) { const float* vertex_uvw = vertex_data + 3 /* components */ * n_vertex; float sum_uvw = vertex_uvw[0] + vertex_uvw[1] + vertex_uvw[2]; if (de::abs(sum_uvw - 1.0f) > epsilon) { m_testCtx.getLog() << tcu::TestLog::Message << "For triangles, U+V+W coordinates outputted " "by tessellator should sum up to 1.0. Instead, the " "following coordinates:" << " (" << vertex_uvw[0] << ", " << vertex_uvw[1] << ", " << vertex_uvw[2] << ") " "sum up to " << sum_uvw << "." << tcu::TestLog::EndMessage; TCU_FAIL("U+V+W coordinates do not add up to 1, even though triangle/tessellation" " was requested"); } } /* for (all vertices) */ } /* if (we're dealing with triangles or quads) */ /* (test 2): Make sure that u, v, w e <0, 1> (always applicable) */ for (unsigned int n_vertex = 0; n_vertex < n_result_vertices; ++n_vertex) { const float* vertex_uvw = vertex_data + 3 /* components */ * n_vertex; if (!(vertex_uvw[0] >= 0.0f && vertex_uvw[0] <= 1.0f && vertex_uvw[1] >= 0.0f && vertex_uvw[1] <= 1.0f && vertex_uvw[2] >= 0.0f && vertex_uvw[2] <= 1.0f)) { m_testCtx.getLog() << tcu::TestLog::Message << "U, V and W coordinates outputted by the tessellator should " "be within <0, 1> range. However, " << "vertex at index: " << n_vertex << "is defined by the following triple:" << " (" << vertex_uvw[0] << ", " << vertex_uvw[1] << ", " << vertex_uvw[2] << ")." << tcu::TestLog::EndMessage; TCU_FAIL("U/V/W coordinate outputted by the tessellator is outside allowed range."); } } /* for (all vertices) */ /* (test 3): Make sure w is always zero (applicable to quads and isolines) */ if (test.primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS || test.primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES) { for (unsigned int n_vertex = 0; n_vertex < n_result_vertices; ++n_vertex) { const float* vertex_uvw = vertex_data + 3 /* components */ * n_vertex; if (de::abs(vertex_uvw[2]) > epsilon) { m_testCtx.getLog() << tcu::TestLog::Message << "W coordinate should be zero for all vertices outputted " "for isolines and quads; for at least one vertex, W was " "found to be equal to: " << vertex_uvw[2] << tcu::TestLog::EndMessage; TCU_FAIL("W coordinate was found to be non-zero for at least one tessellation coordinate" " generated in either quads or isolines primitive mode"); } } /* for (all vertices) */ } /* if (we're dealing with quads or isolines) */ /* Unmap the BO */ gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER); GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer() call failed"); } /* All done */ m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); return STOP; } /** Constructor * * @param context Test context * @param name Test case's name * @param description Test case's desricption **/ TessellationShaderTessellationMaxInOut::TessellationShaderTessellationMaxInOut(Context& context, const ExtParameters& extParams) : TestCaseBase(context, extParams, "max_in_out_attributes", "Make sure it is possible to use up GL_MAX_TESS_*_COMPONENTS_EXT.") , m_po_id_1(0) , m_po_id_2(0) , m_fs_id(0) , m_tcs_id_1(0) , m_tcs_id_2(0) , m_tes_id_1(0) , m_tes_id_2(0) , m_vs_id_1(0) , m_vs_id_2(0) , m_tf_bo_id_1(0) , m_tf_bo_id_2(0) , m_patch_data_bo_id(0) , m_vao_id(0) , m_gl_max_tess_control_input_components_value(0) , m_gl_max_tess_control_output_components_value(0) , m_gl_max_tess_evaluation_input_components_value(0) , m_gl_max_tess_evaluation_output_components_value(0) , m_gl_max_transform_feedback_interleaved_components_value(0) , m_gl_max_tess_patch_components_value(0) , m_gl_max_vertex_output_components_value(0) , m_ref_vertex_attributes(DE_NULL) , m_tf_varyings_names(DE_NULL) { m_ref_patch_attributes[0] = 0.0f; m_ref_patch_attributes[1] = 0.0f; m_ref_patch_attributes[2] = 0.0f; m_ref_patch_attributes[3] = 0.0f; } /** Deinitializes all ES objects created for the test. */ void TessellationShaderTessellationMaxInOut::deinit(void) { /* Call base class deinitialization routine */ TestCaseBase::deinit(); if (!m_is_tessellation_shader_supported) { return; } /* Deallocate dynamic arrays */ if (m_ref_vertex_attributes != DE_NULL) { free(m_ref_vertex_attributes); m_ref_vertex_attributes = DE_NULL; } /* Deallocate the varyings array */ if (m_tf_varyings_names != DE_NULL) { for (int i = 0; i < (m_gl_max_tess_evaluation_output_components_value) / 4 - 1 /* gl_Position */; i++) { if (m_tf_varyings_names[i] != DE_NULL) { free(m_tf_varyings_names[i]); m_tf_varyings_names[i] = DE_NULL; } } free(m_tf_varyings_names); m_tf_varyings_names = DE_NULL; } const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Reset GL_PATCH_VERTICES_EXT pname value */ gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, 3); GLU_EXPECT_NO_ERROR(gl.getError(), "glPatchParameteriEXT() failed!"); /* Unbind vertex array object */ gl.bindVertexArray(0); /* Release ES objects */ if (m_fs_id != 0) { gl.deleteShader(m_fs_id); m_fs_id = 0; } if (m_po_id_1 != 0) { gl.deleteProgram(m_po_id_1); m_po_id_1 = 0; } if (m_po_id_2 != 0) { gl.deleteProgram(m_po_id_2); m_po_id_2 = 0; } if (m_tcs_id_1 != 0) { gl.deleteShader(m_tcs_id_1); m_tcs_id_1 = 0; } if (m_tcs_id_2 != 0) { gl.deleteShader(m_tcs_id_2); m_tcs_id_2 = 0; } if (m_tes_id_1 != 0) { gl.deleteShader(m_tes_id_1); m_tes_id_1 = 0; } if (m_tes_id_2 != 0) { gl.deleteShader(m_tes_id_2); m_tes_id_2 = 0; } if (m_vs_id_1 != 0) { gl.deleteShader(m_vs_id_1); m_vs_id_1 = 0; } if (m_vs_id_2 != 0) { gl.deleteShader(m_vs_id_2); m_vs_id_2 = 0; } if (m_tf_bo_id_1 != 0) { gl.deleteBuffers(1, &m_tf_bo_id_1); m_tf_bo_id_1 = 0; } if (m_tf_bo_id_2 != 0) { gl.deleteBuffers(1, &m_tf_bo_id_2); m_tf_bo_id_2 = 0; } if (m_patch_data_bo_id != 0) { gl.deleteBuffers(1, &m_patch_data_bo_id); m_patch_data_bo_id = 0; } if (m_vao_id != 0) { gl.deleteVertexArrays(1, &m_vao_id); m_vao_id = 0; } } /** 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 TessellationShaderTessellationMaxInOut::iterate(void) { /* Retrieve ES entry-points. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Initialize test-specific ES objects. */ initTest(); /* Execute test case 1 */ gl.useProgram(m_po_id_1); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() failed!"); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, /* index */ m_tf_bo_id_1); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase() failed!"); gl.beginTransformFeedback(GL_POINTS); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback() failed"); gl.drawArrays(m_glExtTokens.PATCHES, 0, /* first */ 2); /* count */ GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays() failed"); gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback() failed"); /* Verify the rendered data. */ bool test_passed = true; test_passed &= compareValues("Per-vertex components test ", m_ref_vertex_attributes, m_gl_max_tess_evaluation_output_components_value / 4); /* Execute test case 2 */ gl.useProgram(m_po_id_2); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() failed!"); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, /* index */ m_tf_bo_id_2); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase() failed!"); gl.beginTransformFeedback(GL_POINTS); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback() failed"); gl.drawArrays(m_glExtTokens.PATCHES, 0, /* first */ 2); /* count */ GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays() failed"); gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback() failed"); /* Verify the rendered data */ test_passed &= compareValues("Per-patch components test ", m_ref_patch_attributes, 1 /* amount of output vectors */); /* Test passed. */ if (test_passed) { m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } return STOP; } /** Sets up buffer objects. * * Note the function throws exception should an error occur! **/ void TessellationShaderTessellationMaxInOut::initBufferObjects(void) { /* Retrieve ES entry-points. */ glw::GLint bo_size = 0; const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Transform feedback buffer object for case 1 */ gl.genBuffers(1, &m_tf_bo_id_1); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers() failed!"); gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_tf_bo_id_1); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() failed!"); bo_size = static_cast(2 /* vertices */ * 4 /* components */ * m_gl_max_tess_evaluation_output_components_value / 4 /* attributes */ * sizeof(glw::GLfloat)); /* attribute size */ gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, bo_size, NULL, GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() failed"); /* Transform feedback buffer object for case 2 */ bo_size = 2 * /* vertices */ sizeof(glw::GLfloat) * 4; /* components */ gl.genBuffers(1, &m_tf_bo_id_2); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers() failed!"); gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_tf_bo_id_2); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() failed!"); gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, bo_size, NULL, GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() failed"); /* Set up vertex data buffer storage */ glw::GLfloat vertices[2 /* vertices */ * 4 /* components */]; bo_size = sizeof(vertices); vertices[0] = 0.f; vertices[1] = 0.f; vertices[2] = 0.f; vertices[3] = 1.f; vertices[4] = 1.f; vertices[5] = 1.f; vertices[6] = 1.f; vertices[7] = 1.f; gl.genBuffers(1, &m_patch_data_bo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers() failed!"); gl.bindBuffer(GL_ARRAY_BUFFER, m_patch_data_bo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() failed!"); gl.bufferData(GL_ARRAY_BUFFER, bo_size, vertices, GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() failed!"); gl.enableVertexAttribArray(0); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray() failed!"); gl.vertexAttribPointer(0, /* index */ 4, /* size */ GL_FLOAT, GL_FALSE, /* normalized */ 0, /* stride */ 0); /* pointer */ GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribPointer() failed!"); } /** Initializes the test. * * Note the function throws exception should an error occur! **/ void TessellationShaderTessellationMaxInOut::initProgramObjects(void) { /* Retrieve ES entry-points */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Create program objects. */ m_po_id_1 = gl.createProgram(); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() failed!"); m_po_id_2 = gl.createProgram(); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() failed!"); /* Set up all the shader objects that will be used for the test */ m_vs_id_1 = gl.createShader(GL_VERTEX_SHADER); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader(GL_VERTEX_SHADER) failed!"); m_vs_id_2 = gl.createShader(GL_VERTEX_SHADER); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader(GL_VERTEX_SHADER) failed!"); m_tcs_id_1 = gl.createShader(m_glExtTokens.TESS_CONTROL_SHADER); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader(GL_TESS_CONTROL_SHADER_EXT) failed!"); m_tcs_id_2 = gl.createShader(m_glExtTokens.TESS_CONTROL_SHADER); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader(GL_TESS_CONTROL_SHADER_EXT) failed!"); m_tes_id_1 = gl.createShader(m_glExtTokens.TESS_EVALUATION_SHADER); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader(GL_TESS_EVALUATION_SHADER_EXT) failed!"); m_tes_id_2 = gl.createShader(m_glExtTokens.TESS_EVALUATION_SHADER); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader(GL_TESS_EVALUATION_SHADER_EXT) failed!"); m_fs_id = gl.createShader(GL_FRAGMENT_SHADER); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader(GL_FRAGMENT_SHADER) failed!"); /* Transform Feedback setup for case 1 * * Varyings array: m_tf_varyings_names[i < m_gl_max_tess_evaluation_output_components_value / 4-1] == "Vertex.value[i]" * m_tf_varyings_names[i == m_gl_max_tess_evaluation_output_components_value / 4-1] == "gl_Position" */ const char position_varying[] = "gl_Position"; m_tf_varyings_names = (char**)malloc((m_gl_max_tess_evaluation_output_components_value / 4) * sizeof(char*)); if (m_tf_varyings_names == DE_NULL) { throw tcu::ResourceError("Unable to allocate memory!"); } for (int i = 0; i < (m_gl_max_tess_evaluation_output_components_value) / 4 /* attributes */ - 1 /* gl_Position */; i++) { std::stringstream tf_varying_stream; const char* tf_varying_raw_ptr = DE_NULL; std::string tf_varying_string; tf_varying_stream << "Vertex.value[" << i << "]"; tf_varying_string = tf_varying_stream.str(); tf_varying_raw_ptr = tf_varying_string.c_str(); m_tf_varyings_names[i] = (char*)malloc(strlen(tf_varying_raw_ptr) + 1 /* '\0' */); if (m_tf_varyings_names[i] == DE_NULL) { throw tcu::ResourceError("Unable to allocate memory!"); } memcpy(m_tf_varyings_names[i], tf_varying_raw_ptr, strlen(tf_varying_raw_ptr) + 1); } m_tf_varyings_names[m_gl_max_tess_evaluation_output_components_value / 4 - 1 /* gl_Position */] = (char*)malloc(sizeof(position_varying)); if (m_tf_varyings_names[m_gl_max_tess_evaluation_output_components_value / 4 - 1 /* gl_Position */] == DE_NULL) { throw tcu::ResourceError("Unable to allocate memory!"); } memcpy(m_tf_varyings_names[m_gl_max_tess_evaluation_output_components_value / 4 - 1 /* gl_Position */], position_varying, sizeof(position_varying)); /* Set up XFB */ gl.transformFeedbackVaryings(m_po_id_1, m_gl_max_tess_evaluation_output_components_value / 4, m_tf_varyings_names, GL_INTERLEAVED_ATTRIBS); GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings() failed!"); /* Set up program objects */ const char* vs_code_raw_ptr = m_vs_code; const char* tcs_code_1_raw_ptr = m_tcs_code_1; const char* tes_code_1_raw_ptr = m_tes_code_1; const char* tcs_code_2_raw_ptr = m_tcs_code_2; const char* tes_code_2_raw_ptr = m_tes_code_2; /* Build a program object to test case 1. */ if (!TessellationShaderTessellationMaxInOut::buildProgram(m_po_id_1, m_vs_id_1, 1, &vs_code_raw_ptr, m_tcs_id_1, 1, &tcs_code_1_raw_ptr, m_tes_id_1, 1, &tes_code_1_raw_ptr, m_fs_id, 1, &m_fs_code)) { TCU_FAIL("Could not build first test program object"); } /* Tranform Feedback setup for case 2 */ const char* const tf_varying_2 = "out_value"; gl.transformFeedbackVaryings(m_po_id_2, 1 /* count */, &tf_varying_2, GL_INTERLEAVED_ATTRIBS); GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings() failed!"); /* Build a program object for case 2 */ if (!(TessellationShaderTessellationMaxInOut::buildProgram(m_po_id_2, m_vs_id_2, 1, &vs_code_raw_ptr, m_tcs_id_2, 1, &tcs_code_2_raw_ptr, m_tes_id_2, 1, &tes_code_2_raw_ptr, m_fs_id, 1, &m_fs_code))) { TCU_FAIL("Could not link second test program object"); } } /** Initializes the test. * * Note the function throws exception should an error occur! **/ void TessellationShaderTessellationMaxInOut::initTest(void) { /* Render state setup */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Skip if required extensions are not supported. */ if (!m_is_tessellation_shader_supported) { throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED); } /* Initialize vertex array object */ gl.genVertexArrays(1, &m_vao_id); GLU_EXPECT_NO_ERROR(gl.getError(), "Could not generate vertex array object"); gl.bindVertexArray(m_vao_id); GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding vertex array object!"); /* All tessellation control shaders used by this test assume two * vertices are going to be provided per input patch. */ gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, 2); GLU_EXPECT_NO_ERROR(gl.getError(), "glPatchParameteriEXT() failed!"); /* Carry on with initialization */ retrieveGLConstantValues(); initProgramObjects(); initBufferObjects(); initReferenceValues(); gl.enable(GL_RASTERIZER_DISCARD); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnable(RASTERIZER_DISCARD) failed!"); } /** Initializes reference values that will be compared against * values generated by the program object. * Fills m_ref_vertex_attributes and m_ref_patch_attributes arrays. * These arrays are later used by compareValues() function. **/ void TessellationShaderTessellationMaxInOut::initReferenceValues(void) { /* Allocate vertex attribute array data needed for reference values preparation. */ int max_array_size = de::max(m_gl_max_tess_control_input_components_value, de::max(m_gl_max_tess_control_output_components_value, de::max(m_gl_max_tess_evaluation_input_components_value, m_gl_max_tess_evaluation_output_components_value))); m_ref_vertex_attributes = (glw::GLfloat*)malloc(sizeof(glw::GLfloat) * (max_array_size)); if (m_ref_vertex_attributes == DE_NULL) { throw tcu::ResourceError("Unable to allocate memory!"); } /* We need to create an array consisting of gl_max_tess_evaluation_output_components items. * The array will be filled with the following values: * * reference_value[0], * (...) * reference_value[gl_max_tess_evaluation_output_components / 4 - 2], * reference_gl_Position * * which corresponds to output block defined for Tessellation Evaluation Stage: * * out Vertex * { * vec4 value[(gl_MaxTessControlInputComponents) / 4 - 1]; * } outVertex; * * + gl_Position. */ glw::GLfloat sumInTCS[] = { 0.0f, 0.0f, 0.0f, 0.0f }; glw::GLfloat sumInTES[] = { 0.0f, 0.0f, 0.0f, 0.0f }; for (int i = 0; i < m_gl_max_tess_control_input_components_value - 4; /* gl_Position */ i++) { m_ref_vertex_attributes[i] = (glw::GLfloat)i; } for (int i = 0; i < m_gl_max_tess_control_input_components_value - 4; /* gl_Position */ i++) { sumInTCS[i % 4 /* component selector */] += m_ref_vertex_attributes[i]; } for (int i = 0; i < m_gl_max_tess_control_output_components_value - 4; /* gl_Position */ i++) { m_ref_vertex_attributes[i] = sumInTCS[i % 4] + (glw::GLfloat)i; } for (int i = m_gl_max_tess_control_input_components_value - 4; /* gl_Position */ i < m_gl_max_tess_control_output_components_value - 4; /* gl_Position */ i++) { m_ref_vertex_attributes[i] = (glw::GLfloat)i; } for (int i = 0; i < m_gl_max_tess_evaluation_input_components_value - 4; /* gl_Position */ i++) { sumInTES[i % 4 /* component selector */] += m_ref_vertex_attributes[i]; } for (int i = 0; i < m_gl_max_tess_evaluation_output_components_value - 4; /* gl_Position */ i++) { m_ref_vertex_attributes[i] = sumInTES[i % 4 /* component selector */] + (glw::GLfloat)i; } for (int i = m_gl_max_tess_evaluation_input_components_value - 4; /* gl_Position */ i < m_gl_max_tess_evaluation_output_components_value - 4; /* gl_Position */ i++) { m_ref_vertex_attributes[i] = (glw::GLfloat)i; } /* Store gl_Position reference values (only first vertex will be compared) */ m_ref_vertex_attributes[m_gl_max_tess_evaluation_output_components_value - 4] = 0.0f; m_ref_vertex_attributes[m_gl_max_tess_evaluation_output_components_value - 3] = 0.0f; m_ref_vertex_attributes[m_gl_max_tess_evaluation_output_components_value - 2] = 0.0f; m_ref_vertex_attributes[m_gl_max_tess_evaluation_output_components_value - 1] = 1.0f; /* Set up reference data for case 2 * * Only one output vector will be needed for comparison. */ m_ref_patch_attributes[0] = 0.0f; m_ref_patch_attributes[1] = 0.0f; m_ref_patch_attributes[2] = 0.0f; m_ref_patch_attributes[3] = 0.0f; for (int i = 0; i < m_gl_max_tess_patch_components_value; i++) { m_ref_patch_attributes[i % 4] += (glw::GLfloat)i; } } /** Retrieve OpenGL state and implementation values. * * Note the function throws exception should an error occur! **/ void TessellationShaderTessellationMaxInOut::retrieveGLConstantValues(void) { /* Retrieve ES entry-points. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Query implementation constants */ gl.getIntegerv(GL_MAX_VERTEX_OUTPUT_COMPONENTS, &m_gl_max_vertex_output_components_value); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() failed for GL_MAX_VERTEX_OUTPUT_COMPONENTS pname!"); gl.getIntegerv(m_glExtTokens.MAX_TESS_CONTROL_INPUT_COMPONENTS, &m_gl_max_tess_control_input_components_value); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() failed for GL_MAX_TESS_CONTROL_INPUT_COMPONENTS_EXT pname!"); gl.getIntegerv(m_glExtTokens.MAX_TESS_CONTROL_OUTPUT_COMPONENTS, &m_gl_max_tess_control_output_components_value); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() failed for GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS_EXT pname!"); gl.getIntegerv(m_glExtTokens.MAX_TESS_PATCH_COMPONENTS, &m_gl_max_tess_patch_components_value); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() failed for GL_MAX_TESS_PATCH_COMPONENTS_EXT pname!"); gl.getIntegerv(m_glExtTokens.MAX_TESS_EVALUATION_INPUT_COMPONENTS, &m_gl_max_tess_evaluation_input_components_value); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() failed for GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS_EXT pname!"); gl.getIntegerv(m_glExtTokens.MAX_TESS_EVALUATION_OUTPUT_COMPONENTS, &m_gl_max_tess_evaluation_output_components_value); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() failed for GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS_EXT pname!"); gl.getIntegerv(GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS, &m_gl_max_transform_feedback_interleaved_components_value); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() failed for GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS pname!"); /* Sanity checks */ DE_ASSERT(m_gl_max_vertex_output_components_value != 0); DE_ASSERT(m_gl_max_tess_control_input_components_value != 0); DE_ASSERT(m_gl_max_tess_control_output_components_value != 0); DE_ASSERT(m_gl_max_tess_patch_components_value != 0); DE_ASSERT(m_gl_max_tess_evaluation_input_components_value != 0); DE_ASSERT(m_gl_max_tess_evaluation_output_components_value != 0); DE_ASSERT(m_gl_max_transform_feedback_interleaved_components_value != 0); /* Make sure it is possible to transfer all components through all the stages. * If not, the test may fail, so we throw not supported. */ if (m_gl_max_vertex_output_components_value < m_gl_max_tess_control_input_components_value) { m_testCtx.getLog() << tcu::TestLog::Message << "Warning: GL_MAX_VERTEX_OUTPUT_COMPONENTS value:" << m_gl_max_vertex_output_components_value << " is less than GL_MAX_TESS_CONTROL_INPUT_COMPONENTS_EXT: " << m_gl_max_tess_control_input_components_value << ". It may not be possible to pass all GL_MAX_TESS_CONTROL_INPUT_COMPONENTS_EXT " "per-vertex components from Vertex Shader to Tessellation Control Shader." << tcu::TestLog::EndMessage; throw tcu::NotSupportedError("GL_MAX_VERTEX_OUTPUT_COMPONENTS < GL_MAX_TESS_CONTROL_INPUT_COMPONENTS"); } if (m_gl_max_tess_control_output_components_value != m_gl_max_tess_evaluation_input_components_value) { m_testCtx.getLog() << tcu::TestLog::Message << "Warning: GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS_EXT:" << m_gl_max_tess_control_output_components_value << " is not equal to GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS_EXT:" << m_gl_max_tess_evaluation_input_components_value << ". It may not be possible to pass all GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS_EXT " "per-vertex components from Tessellation Control Shader to Tessellation " "Evaluation Shader." << tcu::TestLog::EndMessage; throw tcu::NotSupportedError("GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS != " "GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS"); } if (m_gl_max_tess_evaluation_output_components_value > m_gl_max_transform_feedback_interleaved_components_value) { m_testCtx.getLog() << tcu::TestLog::Message << "Warning: GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS_EXT:" << m_gl_max_tess_evaluation_output_components_value << " is greater than GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS:" << m_gl_max_transform_feedback_interleaved_components_value << ". It may not be possible to check all GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS_EXT " "per-vertex components from Tessellation Evaluation Shader." << tcu::TestLog::EndMessage; throw tcu::NotSupportedError("GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS > " "GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS"); } } /** Maps buffer object storage bound to GL_TRANSFORM_FEEDBACK_BUFFER binding point into process space * and verifies the downloaded data matches the user-provided reference data. * * Note the function throws exception should an error occur! * * @param description Case description; * @param reference_values Array storing reference data; * @param n_reference_values Number of vec4s available for reading under @param reference_values; * * @return false if the comparison failed, or true otherwise. **/ bool TessellationShaderTessellationMaxInOut::compareValues(char const* description, glw::GLfloat* reference_values, int n_reference_values) { /* Retrieve ES entry-points */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Map the buffer storage into process space. */ glw::GLint bo_size = static_cast(2 /* number of vertices */ * sizeof(glw::GLfloat) * n_reference_values * 4); /* number of components */ glw::GLfloat* resultFloats = (glw::GLfloat*)gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, bo_size, GL_MAP_READ_BIT); GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange() failed"); /* Verify the data */ const glw::GLfloat epsilon = (glw::GLfloat)1e-5f; bool test_passed = true; for (int i = 0; i < n_reference_values * 4 /* number of components */; i += 4 /* number of components */) { if ((de::abs(resultFloats[i] - reference_values[i]) > epsilon) || (de::abs(resultFloats[i + 1] - reference_values[i + 1]) > epsilon) || (de::abs(resultFloats[i + 2] - reference_values[i + 2]) > epsilon) || (de::abs(resultFloats[i + 3] - reference_values[i + 3]) > epsilon)) { m_testCtx.getLog() << tcu::TestLog::Message << description << ": captured results " << "vec4(" << resultFloats[i + 0] << ", " << resultFloats[i + 1] << ", " << resultFloats[i + 2] << ", " << resultFloats[i + 3] << ") " << "are different from the expected values " << "vec4(" << reference_values[i + 0] << ", " << reference_values[i + 1] << ", " << reference_values[i + 2] << ", " << reference_values[i + 3] << ")." << tcu::TestLog::EndMessage; test_passed = false; break; } } gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER); GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer() failed!"); return test_passed; } } /* namespace glcts */