/*------------------------------------------------------------------------- * 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 "esextcTessellationShaderTCTE.hpp" #include "esextcTessellationShaderUtils.hpp" #include "gluContextInfo.hpp" #include "gluDefs.hpp" #include "glwEnums.hpp" #include "glwFunctions.hpp" #include "tcuTestLog.hpp" #include namespace glcts { /** Constructor * * @param context Test context **/ TessellationShaderTCTETests::TessellationShaderTCTETests(glcts::Context& context, const ExtParameters& extParams) : TestCaseGroupBase(context, extParams, "tessellation_control_to_tessellation_evaluation", "Verifies various aspects of communication between tessellation " "control and tessellation evaluation stages") { /* No implementation needed */ } /** * Initializes test groups for geometry shader tests **/ void TessellationShaderTCTETests::init(void) { addChild(new glcts::TessellationShaderTCTEDataPassThrough(m_context, m_extParams)); addChild(new glcts::TessellationShaderTCTEgl_in(m_context, m_extParams)); addChild(new glcts::TessellationShaderTCTEgl_MaxPatchVertices_Position_PointSize(m_context, m_extParams)); addChild(new glcts::TessellationShaderTCTEgl_PatchVerticesIn(m_context, m_extParams)); addChild(new glcts::TessellationShaderTCTEgl_TessLevel(m_context, m_extParams)); } /** Constructor * * @param context Test context **/ TessellationShaderTCTEDataPassThrough::TessellationShaderTCTEDataPassThrough(Context& context, const ExtParameters& extParams) : TestCaseBase(context, extParams, "data_pass_through", "Verifies data is correctly passed down the VS->TC->TS->(GS) pipeline.") , m_bo_id(0) , m_n_input_vertices_per_run(4) , m_utils_ptr(DE_NULL) , m_vao_id(0) { /* Left blank on purpose */ } /** Deinitializes all ES objects created for the test. */ void TessellationShaderTCTEDataPassThrough::deinit() { /** Call base class' deinit() function */ TestCaseBase::deinit(); if (!m_is_tessellation_shader_supported) { return; } const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Revert GL_PATCH_VERTICES_EXT value to the default setting */ gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, 3); /* Disable GL_RASTERIZER_DISCARD mode */ gl.disable(GL_RASTERIZER_DISCARD); /* Revert TF buffer object bindings */ gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* buffer */); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* index */, 0 /* buffer */); /* Unbind vertex array object */ gl.bindVertexArray(0); /* Release all objects we might've created */ if (m_bo_id != 0) { gl.deleteBuffers(1, &m_bo_id); m_bo_id = 0; } if (m_vao_id != 0) { gl.deleteVertexArrays(1, &m_vao_id); m_vao_id = 0; } for (_runs::iterator it = m_runs.begin(); it != m_runs.end(); ++it) { deinitTestRun(*it); } m_runs.clear(); /* Release Utils instance */ if (m_utils_ptr != DE_NULL) { delete m_utils_ptr; m_utils_ptr = DE_NULL; } } /** Deinitializes all ES object created for a specific test run. **/ void TessellationShaderTCTEDataPassThrough::deinitTestRun(_run& run) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); if (run.fs_id != 0) { gl.deleteShader(run.fs_id); run.fs_id = 0; } if (run.gs_id != 0) { gl.deleteShader(run.gs_id); run.gs_id = 0; } if (run.po_id != 0) { gl.deleteProgram(run.po_id); run.po_id = 0; } if (run.tcs_id != 0) { gl.deleteShader(run.tcs_id); run.tcs_id = 0; } if (run.tes_id != 0) { gl.deleteShader(run.tes_id); run.tes_id = 0; } if (run.vs_id != 0) { gl.deleteShader(run.vs_id); run.vs_id = 0; } } /** Initializes all ES objects that will be used for the test. */ void TessellationShaderTCTEDataPassThrough::initTest() { /* The test requires EXT_tessellation_shader */ if (!m_is_tessellation_shader_supported) { throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED); } /* Create an 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!"); /* Our program objects take a single vertex per patch */ gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, 1); GLU_EXPECT_NO_ERROR(gl.getError(), "glPatchParameteriEXT() call failed"); /* Disable rasterization */ gl.enable(GL_RASTERIZER_DISCARD); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnable(GL_RASTERIZER_DISCARD) failed"); /* Create a buffer object we will use for XFB */ gl.genBuffers(1, &m_bo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers() failed"); /* Set up XFB buffer object 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"); /* Prepare 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]); /* Iterate over all supported primitive modes */ for (unsigned int n_primitive_mode = 0; n_primitive_mode < n_primitive_modes; ++n_primitive_mode) { _tessellation_primitive_mode primitive_mode = primitive_modes[n_primitive_mode]; /* If geometry shaders are supported, include a separate iteration to include them * in the pipeline */ for (int n_gs_stage_usage = 0; n_gs_stage_usage < ((m_is_geometry_shader_extension_supported) ? 2 : 1); ++n_gs_stage_usage) { bool use_gs_stage = (n_gs_stage_usage == 1); /* If geometry shaders support gl_PointSize, include a separate iteration to pass * point size data as well */ for (int n_gs_pointsize_usage = 0; n_gs_pointsize_usage < ((m_is_geometry_shader_point_size_supported) ? 2 : 1); ++n_gs_pointsize_usage) { bool use_gs_pointsize_data = (n_gs_pointsize_usage == 1); /* If tessellation shaders support gl_PointSize, include a separate iteration to pass * point size data as well */ for (int n_ts_pointsize_usage = 0; n_ts_pointsize_usage < ((m_is_tessellation_shader_point_size_supported) ? 2 : 1); ++n_ts_pointsize_usage) { bool use_ts_pointsize_data = (n_ts_pointsize_usage == 1); /* Note: it does not make sense to try to pass gl_PointSize data * in geometry stage if tessellation stage did not provide it. */ if (!use_ts_pointsize_data && use_gs_pointsize_data) { continue; } /* Initialize test run data */ _run run; executeTestRun(run, primitive_mode, use_gs_stage, use_gs_pointsize_data, use_ts_pointsize_data); /* Store the run for later usage */ m_runs.push_back(run); } /* for (tessellation point size data usage off and on cases) */ } /* for (geometry point size data usage off and on cases) */ } /* for (GS stage usage) */ } /* for (all primitive modes) */ } /** Initializes a test run, executes it and gathers all the rendered data for further * processing. Extracted data is stored in the run descriptor. * * @param run Test run descriptor to fill with ES object data, * as well as generated data. * @param primitive_mode Primitive mode to use for the test run. * @param should_use_geometry_shader true if the test run should use Geometry Shader stage, * false otherwise. * @param should_pass_point_size_data_in_gs true if the test run should define two output variables * in Geometry Shader, later set to gl_PointSize values from * TC and TE stages. False to skip them. * Only set to true if GL_EXT_geometry_point_size extension * is supported. * @param should_pass_point_size_data_in_ts true if the test run should define two output variables * in both Tessellation Shader types, set to gl_PointSize values * as accessible during execution. False to skip the definitions. * Only set to true if GL_EXT_tessellation_point_size extension * is supported. */ void TessellationShaderTCTEDataPassThrough::executeTestRun(_run& run, _tessellation_primitive_mode primitive_mode, bool should_use_geometry_shader, bool should_pass_point_size_data_in_gs, bool should_pass_point_size_data_in_ts) { run.primitive_mode = primitive_mode; /* Retrieve ES entry-points before we start */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Create a program object first */ run.po_id = gl.createProgram(); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() failed"); /* Create all shader objects we wil be later attaching to the program object */ run.fs_id = gl.createShader(GL_FRAGMENT_SHADER); run.tcs_id = gl.createShader(m_glExtTokens.TESS_CONTROL_SHADER); run.tes_id = gl.createShader(m_glExtTokens.TESS_EVALUATION_SHADER); run.vs_id = gl.createShader(GL_VERTEX_SHADER); if (should_use_geometry_shader) { run.gs_id = gl.createShader(m_glExtTokens.GEOMETRY_SHADER); } GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() call(s) failed"); /* Attach the shader objects to the program object */ gl.attachShader(run.po_id, run.fs_id); gl.attachShader(run.po_id, run.tcs_id); gl.attachShader(run.po_id, run.tes_id); gl.attachShader(run.po_id, run.vs_id); if (should_use_geometry_shader) { gl.attachShader(run.po_id, run.gs_id); } GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader() call(s) failed"); /* Set vertex shader's body */ const char* vs_body = "${VERSION}\n" "\n" "${SHADER_IO_BLOCKS_REQUIRE}\n" "\n" "out OUT_VS\n" "{\n" " vec4 value1;\n" " ivec4 value2;\n" "} out_data;\n" "\n" "void main()\n" "{\n" " gl_Position = vec4( float(gl_VertexID) );\n" " gl_PointSize = 1.0 / float(gl_VertexID + 1);\n" " out_data.value1 = vec4(float(gl_VertexID), float(gl_VertexID) * 0.5,\n" " float(gl_VertexID) * 0.25, float(gl_VertexID) * 0.125);\n" " out_data.value2 = ivec4(gl_VertexID, gl_VertexID + 1,\n" " gl_VertexID + 2, gl_VertexID + 3);\n" "}\n"; shaderSourceSpecialized(run.vs_id, 1 /* count */, &vs_body); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() call failed for vertex shader"); /* Set minimal fragment shader's body */ const char* fs_body = "${VERSION}\n" "\n" "void main()\n" "{\n" "}\n"; shaderSourceSpecialized(run.fs_id, 1 /* count */, &fs_body); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() call failed for fragment shader"); /* Set tessellation control shader's body */ { std::stringstream body_sstream; std::string body_string; const char* body_raw_ptr = DE_NULL; body_sstream << "${VERSION}\n" "\n" "${TESSELLATION_SHADER_REQUIRE}\n"; if (should_pass_point_size_data_in_ts) { body_sstream << "${TESSELLATION_POINT_SIZE_REQUIRE}\n"; } body_sstream << "\n" "layout(vertices = 2) out;\n" "\n" "in OUT_VS\n" "{\n" " vec4 value1;\n" " ivec4 value2;\n" "} in_vs_data[];\n" "\n" "out OUT_TC\n" "{\n"; if (should_pass_point_size_data_in_ts) { body_sstream << " float tc_pointSize;\n"; } body_sstream << " vec4 tc_position;\n" " vec4 tc_value1;\n" " ivec4 tc_value2;\n" "} out_data[];\n" "\n" "patch out vec4 tc_patch_data;\n" "\n" "void main()\n" "{\n" " int multiplier = 1;\n" "\n" " if (gl_InvocationID == 0)\n" " {\n" " multiplier = 2;\n" " }\n"; if (should_pass_point_size_data_in_ts) { body_sstream << " out_data [gl_InvocationID].tc_pointSize = gl_in[0].gl_PointSize;\n" " gl_out [gl_InvocationID].gl_PointSize = gl_in[0].gl_PointSize * 2.0;\n"; } body_sstream << " out_data [gl_InvocationID].tc_position = gl_in [0].gl_Position;\n" " out_data [gl_InvocationID].tc_value1 = in_vs_data[0].value1 * " "vec4(float(multiplier) );\n" " out_data [gl_InvocationID].tc_value2 = in_vs_data[0].value2 * ivec4( " "multiplier);\n" " gl_out [gl_InvocationID].gl_Position = gl_in [0].gl_Position + vec4(3.0);\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" " if (gl_InvocationID == 0)\n" " {\n" " tc_patch_data = in_vs_data[0].value1 * vec4(float(multiplier) );\n" " }\n" "}\n"; body_string = body_sstream.str(); body_raw_ptr = body_string.c_str(); shaderSourceSpecialized(run.tcs_id, 1 /* count */, &body_raw_ptr); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() call failed for tessellation control shader"); } /* Set tessellation evaluation shader's body */ { std::stringstream body_sstream; std::string body_string; const char* body_raw_ptr = DE_NULL; /* Preamble */ body_sstream << "${VERSION}\n" "\n" "${TESSELLATION_SHADER_REQUIRE}\n"; if (should_pass_point_size_data_in_ts) { body_sstream << "${TESSELLATION_POINT_SIZE_REQUIRE}\n"; } /* Layout qualifiers */ body_sstream << "\n" "layout(PRIMITIVE_MODE, point_mode) in;\n" "\n" /* Input block definition starts here: */ "in OUT_TC\n" "{\n"; if (should_pass_point_size_data_in_ts) { body_sstream << " float tc_pointSize;\n"; } body_sstream << " vec4 tc_position;\n" " vec4 tc_value1;\n" " ivec4 tc_value2;\n" "} in_data[];\n" "\n" "patch in vec4 tc_patch_data;\n" "\n"; /* Input block definition ends here. */ /* Output block definition (only defined if GS stage is present) starts here: */ if (should_use_geometry_shader) { body_sstream << "out OUT_TE\n" "{\n"; /* Output block contents */ if (should_pass_point_size_data_in_ts) { body_sstream << " float tc_pointSize;\n" " float te_pointSize;\n"; } body_sstream << " vec4 tc_position;\n" " vec4 tc_value1;\n" " ivec4 tc_value2;\n" " vec4 te_position;\n" "} out_data;\n"; } /* Output block definition ends here. */ else { if (should_pass_point_size_data_in_ts) { body_sstream << "out float tc_pointSize;\n" "out float te_pointSize;\n"; } body_sstream << "out vec4 tc_position;\n" "out vec4 tc_value1;\n" "flat out ivec4 tc_value2;\n" "out vec4 te_position;\n" "out vec4 te_patch_data;\n"; } body_sstream << "\n" "void main()\n" "{\n"; if (should_use_geometry_shader) { body_sstream << "#define OUTPUT_VARIABLE(x) out_data.x\n"; } else { body_sstream << "#define OUTPUT_VARIABLE(x) x\n"; } if (should_pass_point_size_data_in_ts) { body_sstream << " OUTPUT_VARIABLE(tc_pointSize) = in_data[1].tc_pointSize;\n" " OUTPUT_VARIABLE(te_pointSize) = gl_in[1].gl_PointSize;\n"; } body_sstream << " OUTPUT_VARIABLE(tc_position) = in_data[1].tc_position;\n" " OUTPUT_VARIABLE(tc_value1) = in_data[0].tc_value1;\n" " OUTPUT_VARIABLE(tc_value2) = in_data[1].tc_value2;\n" " OUTPUT_VARIABLE(te_position) = gl_in[0].gl_Position;\n"; if (!should_use_geometry_shader) { body_sstream << " OUTPUT_VARIABLE(te_patch_data) = tc_patch_data;\n"; } body_sstream << "}\n"; body_string = body_sstream.str(); /* Replace PRIMITIVE_MODE token with user-requested primitive mode */ std::string primitive_mode_replacement = TessellationShaderUtils::getESTokenForPrimitiveMode(primitive_mode); std::string primitive_mode_token = "PRIMITIVE_MODE"; std::size_t primitive_mode_token_position = std::string::npos; primitive_mode_token_position = body_string.find(primitive_mode_token); while (primitive_mode_token_position != std::string::npos) { body_string = body_string.replace(primitive_mode_token_position, primitive_mode_token.length(), primitive_mode_replacement); primitive_mode_token_position = body_string.find(primitive_mode_token); } body_raw_ptr = body_string.c_str(); shaderSourceSpecialized(run.tes_id, 1 /* count */, &body_raw_ptr); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() call failed for tessellation evaluation shader"); } /* Set geometry shader's body (if requested) */ if (should_use_geometry_shader) { std::stringstream body_sstream; std::string body_string; const char* body_raw_ptr = DE_NULL; body_sstream << "${VERSION}\n" "\n" "${GEOMETRY_SHADER_REQUIRE}\n"; if (should_pass_point_size_data_in_gs) { body_sstream << "${GEOMETRY_POINT_SIZE_REQUIRE}\n"; } body_sstream << "${SHADER_IO_BLOCKS_REQUIRE}\n" "\n" "layout(points) in;\n" "layout(max_vertices = 2, points) out;\n" "\n" "in OUT_TE\n" "{\n"; if (should_pass_point_size_data_in_ts) { body_sstream << " float tc_pointSize;\n" " float te_pointSize;\n"; } body_sstream << " vec4 tc_position;\n" " vec4 tc_value1;\n" " ivec4 tc_value2;\n" " vec4 te_position;\n" "} in_data[1];\n" "\n" "out float gs_tc_pointSize;\n" "out float gs_te_pointSize;\n" "out vec4 gs_tc_position;\n" "out vec4 gs_tc_value1;\n" "flat out ivec4 gs_tc_value2;\n" "out vec4 gs_te_position;\n" "\n" "void main()\n" "{\n"; if (should_pass_point_size_data_in_gs) { body_sstream << " gs_tc_pointSize = in_data[0].tc_pointSize;\n" " gs_te_pointSize = in_data[0].te_pointSize;\n"; } body_sstream << " gs_tc_position = in_data[0].tc_position;\n" " gs_tc_value1 = in_data[0].tc_value1;\n" " gs_tc_value2 = in_data[0].tc_value2;\n" " gs_te_position = in_data[0].te_position;\n" " EmitVertex();\n"; if (should_pass_point_size_data_in_gs) { body_sstream << " gs_tc_pointSize = in_data[0].tc_pointSize + 1.0;\n" " gs_te_pointSize = in_data[0].te_pointSize + 1.0;\n"; } body_sstream << " gs_tc_position = in_data[0].tc_position + vec4(1.0);\n" " gs_tc_value1 = in_data[0].tc_value1 + vec4(1.0);\n" " gs_tc_value2 = in_data[0].tc_value2 + ivec4(1);\n" " gs_te_position = in_data[0].te_position + vec4(1.0);\n" "\n" " EmitVertex();\n" "\n" "}\n"; body_string = body_sstream.str(); body_raw_ptr = body_string.c_str(); shaderSourceSpecialized(run.gs_id, 1 /* count */, &body_raw_ptr); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() call failed for geometry shader"); } /* Configure varyings */ unsigned int n_varyings = 0; int varying_tc_pointSize_offset = -1; int varying_tc_position_offset = -1; int varying_tc_value1_offset = -1; int varying_tc_value2_offset = -1; int varying_te_patch_data_offset = -1; int varying_te_pointSize_offset = -1; int varying_te_position_offset = -1; const unsigned int varying_patch_data_size = sizeof(float) * 4; /* vec4 */ const unsigned int varying_pointSize_size = sizeof(float); const unsigned int varying_position_size = sizeof(float) * 4; /* vec4 */ const unsigned int varying_value1_size = sizeof(float) * 4; /* vec4 */ const unsigned int varying_value2_size = sizeof(int) * 4; /* ivec4 */ const char** varyings = DE_NULL; unsigned int varyings_size = 0; const char* gs_non_point_size_varyings[] = { "gs_tc_position", "gs_tc_value1", "gs_tc_value2", "gs_te_position" }; const char* gs_point_size_varyings[] = { "gs_tc_position", "gs_tc_value1", "gs_tc_value2", "gs_te_position", "gs_tc_pointSize", "gs_te_pointSize" }; const char* non_gs_non_point_size_varyings[] = { "tc_position", "tc_value1", "tc_value2", "te_position", "te_patch_data" }; const char* non_gs_point_size_varyings[] = { "tc_position", "tc_value1", "tc_value2", "te_position", "tc_pointSize", "te_pointSize", "te_patch_data" }; if (should_use_geometry_shader) { if (should_pass_point_size_data_in_gs) { n_varyings = sizeof(gs_point_size_varyings) / sizeof(gs_point_size_varyings[0]); varyings = gs_point_size_varyings; varyings_size = varying_position_size + /* gs_tc_position */ varying_value1_size + /* gs_tc_value1 */ varying_value2_size + /* gs_tc_value2 */ varying_position_size + /* gs_te_position */ varying_pointSize_size + /* gs_tc_pointSize */ varying_pointSize_size; /* gs_te_pointSize */ varying_tc_position_offset = 0; varying_tc_value1_offset = varying_tc_position_offset + varying_position_size; varying_tc_value2_offset = varying_tc_value1_offset + varying_value1_size; varying_te_position_offset = varying_tc_value2_offset + varying_value2_size; varying_tc_pointSize_offset = varying_te_position_offset + varying_position_size; varying_te_pointSize_offset = varying_tc_pointSize_offset + varying_pointSize_size; } else { n_varyings = sizeof(gs_non_point_size_varyings) / sizeof(gs_non_point_size_varyings[0]); varyings = gs_non_point_size_varyings; varyings_size = varying_position_size + /* gs_tc_position */ varying_value1_size + /* gs_tc_value1 */ varying_value2_size + /* gs_tc_value2 */ varying_position_size; /* gs_te_position */ varying_tc_position_offset = 0; varying_tc_value1_offset = varying_tc_position_offset + varying_position_size; varying_tc_value2_offset = varying_tc_value1_offset + varying_value1_size; varying_te_position_offset = varying_tc_value2_offset + varying_value2_size; } } /* if (should_use_geometry_shader) */ else { if (should_pass_point_size_data_in_ts) { n_varyings = sizeof(non_gs_point_size_varyings) / sizeof(non_gs_point_size_varyings[0]); varyings = non_gs_point_size_varyings; varyings_size = varying_position_size + /* tc_position */ varying_value1_size + /* tc_value1 */ varying_value2_size + /* tc_value2 */ varying_position_size + /* te_position */ varying_pointSize_size + /* tc_pointSize */ varying_pointSize_size + /* te_pointSize */ varying_patch_data_size; /* tc_patch_data */ varying_tc_position_offset = 0; varying_tc_value1_offset = varying_tc_position_offset + varying_position_size; varying_tc_value2_offset = varying_tc_value1_offset + varying_value1_size; varying_te_position_offset = varying_tc_value2_offset + varying_value2_size; varying_tc_pointSize_offset = varying_te_position_offset + varying_position_size; varying_te_pointSize_offset = varying_tc_pointSize_offset + varying_pointSize_size; varying_te_patch_data_offset = varying_te_pointSize_offset + varying_pointSize_size; } else { n_varyings = sizeof(non_gs_non_point_size_varyings) / sizeof(non_gs_non_point_size_varyings[0]); varyings = non_gs_non_point_size_varyings; varyings_size = varying_position_size + /* tc_position */ varying_value1_size + /* tc_value1 */ varying_value2_size + /* tc_value2 */ varying_position_size + /* te_position */ varying_patch_data_size; /* tc_patch_data */ varying_tc_position_offset = 0; varying_tc_value1_offset = varying_tc_position_offset + varying_position_size; varying_tc_value2_offset = varying_tc_value1_offset + varying_value1_size; varying_te_position_offset = varying_tc_value2_offset + varying_value2_size; varying_te_patch_data_offset = varying_te_position_offset + varying_position_size; } } gl.transformFeedbackVaryings(run.po_id, n_varyings, varyings, GL_INTERLEAVED_ATTRIBS); GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings() call failed"); /* Compile all the shader objects */ const glw::GLuint shaders[] = { run.fs_id, run.gs_id, run.tcs_id, run.tes_id, run.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) { m_utils_ptr->compileShaders(1 /* n_shaders */, &shader, true); } } /* Link the program object */ gl.linkProgram(run.po_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glLinkProgram() call failed"); /* Make sure the linking has succeeded */ glw::GLint link_status = GL_FALSE; 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"); } /* Now that we have a linked program object, it's time to determine how much space * we will need to hold XFB data. */ unsigned int bo_size = 0; unsigned int n_result_tess_coords = 0; const float tess_levels[] = /* as per shaders constructed by the test */ { 4.0f, 4.0f, 4.0f, 4.0f }; n_result_tess_coords = m_utils_ptr->getAmountOfVerticesGeneratedByTessellator( run.primitive_mode, tess_levels, tess_levels, TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, true); /* is_point_mode_enabled */ if (should_use_geometry_shader) { /* Geometry shader will output twice as many vertices */ n_result_tess_coords *= 2; } run.n_result_vertices_per_patch = n_result_tess_coords; n_result_tess_coords *= m_n_input_vertices_per_run; bo_size = n_result_tess_coords * varyings_size; /* Proceed with buffer object storage allocation */ gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, bo_size, DE_NULL, /* data */ GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() call failed"); /* Great, time to actually render the data! */ glw::GLenum tf_mode = TessellationShaderUtils::getTFModeForPrimitiveMode(run.primitive_mode, true); /* is_point_mode_enabled */ gl.useProgram(run.po_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() failed"); gl.beginTransformFeedback(tf_mode); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback() failed"); { gl.drawArrays(m_glExtTokens.PATCHES, 0 /* first */, m_n_input_vertices_per_run); GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays() failed"); } gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback() failed"); /* The data should have landed in the buffer object storage by now. Map the BO into * process space. */ const void* bo_ptr = gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, /* offset */ bo_size, GL_MAP_READ_BIT); GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange() failed"); /* Extract varyings' data */ for (unsigned int n_tess_coord = 0; n_tess_coord < n_result_tess_coords; ++n_tess_coord) { const char* data = (const char*)bo_ptr + n_tess_coord * varyings_size; if (varying_tc_position_offset != -1) { const float* position_data((const float*)(data + varying_tc_position_offset)); _vec4 new_entry(position_data[0], position_data[1], position_data[2], position_data[3]); run.result_tc_position_data.push_back(new_entry); } if (varying_tc_value1_offset != -1) { const float* value1_data((const float*)(data + varying_tc_value1_offset)); _vec4 new_entry(value1_data[0], value1_data[1], value1_data[2], value1_data[3]); run.result_tc_value1_data.push_back(new_entry); } if (varying_tc_value2_offset != -1) { const int* value2_data((const int*)(data + varying_tc_value2_offset)); _ivec4 new_entry(value2_data[0], value2_data[1], value2_data[2], value2_data[3]); run.result_tc_value2_data.push_back(new_entry); } if (varying_te_position_offset != -1) { const float* position_data((const float*)(data + varying_te_position_offset)); _vec4 new_entry(position_data[0], position_data[1], position_data[2], position_data[3]); run.result_te_position_data.push_back(new_entry); } if (varying_tc_pointSize_offset != -1) { const float* pointSize_ptr((const float*)(data + varying_tc_pointSize_offset)); run.result_tc_pointSize_data.push_back(*pointSize_ptr); } if (varying_te_pointSize_offset != -1) { const float* pointSize_ptr((const float*)(data + varying_te_pointSize_offset)); run.result_te_pointSize_data.push_back(*pointSize_ptr); } if (varying_te_patch_data_offset != -1) { const float* patch_data_ptr((const float*)(data + varying_te_patch_data_offset)); _vec4 new_entry(patch_data_ptr[0], patch_data_ptr[1], patch_data_ptr[2], patch_data_ptr[3]); run.result_te_patch_data.push_back(new_entry); } } /* for (all XFB data associated with tessellated coordinates) */ /* Now that we're done extracting the data we need, we're fine to unmap the buffer object */ gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER); GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer() 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 TessellationShaderTCTEDataPassThrough::iterate(void) { const float epsilon = 1e-5f; /* Initialize ES test objects */ initTest(); /* Iterate over all runs */ for (_runs_const_iterator run_iterator = m_runs.begin(); run_iterator != m_runs.end(); ++run_iterator) { const _run& run = *run_iterator; /* Check result tc_pointSize data if available */ unsigned int n_vertex = 0; for (std::vector::const_iterator data_iterator = run.result_tc_pointSize_data.begin(); data_iterator != run.result_tc_pointSize_data.end(); data_iterator++, n_vertex++) { const glw::GLfloat data = *data_iterator; unsigned int vertex_id = n_vertex / run.n_result_vertices_per_patch; float expected_value = 1.0f / (float(vertex_id) + 1.0f); if (run.gs_id != 0 && (n_vertex % 2) != 0) { /* Odd vertices emitted by geometry shader add 1 to all components */ expected_value += 1.0f; } if (de::abs(data - expected_value) > epsilon) { m_testCtx.getLog() << tcu::TestLog::Message << "Invalid tc_pointSize value found at index [" << n_vertex << "];" " expected:[" << expected_value << "], " " found:[" << data << "]." << tcu::TestLog::EndMessage; TCU_FAIL("Invalid tc_pointSize value found"); } } /* Check result tc_position data if available */ n_vertex -= n_vertex; for (std::vector<_vec4>::const_iterator data_iterator = run.result_tc_position_data.begin(); data_iterator != run.result_tc_position_data.end(); data_iterator++, n_vertex++) { const _vec4& data = *data_iterator; float expected_value = (float)(n_vertex / run.n_result_vertices_per_patch); if (run.gs_id != 0 && (n_vertex % 2) != 0) { /* Odd vertices emitted by geometry shader add 1 to all components */ expected_value += 1.0f; } if (de::abs(data.x - expected_value) > epsilon || de::abs(data.y - expected_value) > epsilon || de::abs(data.z - expected_value) > epsilon || de::abs(data.w - expected_value) > epsilon) { m_testCtx.getLog() << tcu::TestLog::Message << "Invalid tc_position value found at index [" << n_vertex << "];" " expected:" " [" << expected_value << ", " << expected_value << ", " << expected_value << ", " << expected_value << "], found:" " [" << data.x << ", " << data.y << ", " << data.z << ", " << data.w << "]." << tcu::TestLog::EndMessage; TCU_FAIL("Invalid tc_position value found"); } } /* Check result tc_value1 data if available */ n_vertex -= n_vertex; for (std::vector<_vec4>::const_iterator data_iterator = run.result_tc_value1_data.begin(); data_iterator != run.result_tc_value1_data.end(); data_iterator++, n_vertex++) { const _vec4& data = *data_iterator; unsigned int vertex_id = n_vertex / run.n_result_vertices_per_patch; _vec4 expected_value = _vec4((float)vertex_id, ((float)vertex_id) * 0.5f, ((float)vertex_id) * 0.25f, ((float)vertex_id) * 0.125f); /* TE uses an even vertex outputted by TC, so we need * to multiply the expected value by 2. */ expected_value.x *= 2.0f; expected_value.y *= 2.0f; expected_value.z *= 2.0f; expected_value.w *= 2.0f; if (run.gs_id != 0 && (n_vertex % 2) != 0) { /* Odd vertices emitted by geometry shader add 1 to all components */ expected_value.x += 1.0f; expected_value.y += 1.0f; expected_value.z += 1.0f; expected_value.w += 1.0f; } if (de::abs(data.x - expected_value.x) > epsilon || de::abs(data.y - expected_value.y) > epsilon || de::abs(data.z - expected_value.z) > epsilon || de::abs(data.w - expected_value.w) > epsilon) { m_testCtx.getLog() << tcu::TestLog::Message << "Invalid tc_value1 value found at index [" << n_vertex << "];" " expected:" " [" << expected_value.x << ", " << expected_value.y << ", " << expected_value.z << ", " << expected_value.w << "], found:" " [" << data.x << ", " << data.y << ", " << data.z << ", " << data.w << "]." << tcu::TestLog::EndMessage; TCU_FAIL("Invalid tc_value1 value found"); } } /* Check result tc_value2 data if available */ n_vertex -= n_vertex; for (std::vector<_ivec4>::const_iterator data_iterator = run.result_tc_value2_data.begin(); data_iterator != run.result_tc_value2_data.end(); data_iterator++, n_vertex++) { const _ivec4& data = *data_iterator; unsigned int vertex_id = n_vertex / run.n_result_vertices_per_patch; _ivec4 expected_value = _ivec4(vertex_id, vertex_id + 1, vertex_id + 2, vertex_id + 3); if (run.gs_id != 0 && (n_vertex % 2) != 0) { /* Odd vertices emitted by geometry shader add 1 to all components */ expected_value.x++; expected_value.y++; expected_value.z++; expected_value.w++; } if (data.x != expected_value.x || data.y != expected_value.y || data.z != expected_value.z || data.w != expected_value.w) { m_testCtx.getLog() << tcu::TestLog::Message << "Invalid tc_value2 value found at index [" << n_vertex << "];" " expected:" " [" << expected_value.x << ", " << expected_value.y << ", " << expected_value.z << ", " << expected_value.w << "], found:" " [" << data.x << ", " << data.y << ", " << data.z << ", " << data.w << "]." << tcu::TestLog::EndMessage; TCU_FAIL("Invalid tc_value2 value found"); } } /* Check result te_pointSize data if available */ n_vertex -= n_vertex; for (std::vector::const_iterator data_iterator = run.result_te_pointSize_data.begin(); data_iterator != run.result_te_pointSize_data.end(); data_iterator++, n_vertex++) { const glw::GLfloat data = *data_iterator; unsigned int vertex_id = n_vertex / run.n_result_vertices_per_patch; float expected_value = 2.0f / (float(vertex_id) + 1.0f); if (run.gs_id != 0 && (n_vertex % 2) != 0) { /* Odd vertices emitted by geometry shader add 1 to all components */ expected_value += 1.0f; } if (de::abs(data - expected_value) > epsilon) { m_testCtx.getLog() << tcu::TestLog::Message << "Invalid te_pointSize value found at index [" << n_vertex << "];" " expected:[" << expected_value << "], " " found:[" << data << "]." << tcu::TestLog::EndMessage; TCU_FAIL("Invalid te_pointSize value found"); } } /* Check result te_position data if available */ n_vertex -= n_vertex; for (std::vector<_vec4>::const_iterator data_iterator = run.result_te_position_data.begin(); data_iterator != run.result_te_position_data.end(); data_iterator++, n_vertex++) { const _vec4& data = *data_iterator; float expected_value = (float)(n_vertex / run.n_result_vertices_per_patch); /* te_position should be equal to tc_position, with 3 added to all components */ expected_value += 3.0f; if (run.gs_id != 0 && (n_vertex % 2) != 0) { /* Odd vertices emitted by geometry shader add 1 to all components */ expected_value += 1.0f; } if (de::abs(data.x - expected_value) > epsilon || de::abs(data.y - expected_value) > epsilon || de::abs(data.z - expected_value) > epsilon || de::abs(data.w - expected_value) > epsilon) { m_testCtx.getLog() << tcu::TestLog::Message << "Invalid te_position value found at index [" << n_vertex << "];" " expected:" " [" << expected_value << ", " << expected_value << ", " << expected_value << ", " << expected_value << "], found:" " [" << data.x << ", " << data.y << ", " << data.z << ", " << data.w << "]." << tcu::TestLog::EndMessage; TCU_FAIL("Invalid te_position value found"); } } /* Check result tc_patch_data data if available */ n_vertex -= n_vertex; for (std::vector<_vec4>::const_iterator data_iterator = run.result_te_patch_data.begin(); data_iterator != run.result_te_patch_data.end(); data_iterator++, n_vertex++) { const _vec4& data = *data_iterator; unsigned int vertex_id = n_vertex / run.n_result_vertices_per_patch; _vec4 expected_value = _vec4((float)vertex_id, ((float)vertex_id) * 0.5f, ((float)vertex_id) * 0.25f, ((float)vertex_id) * 0.125f); /* TE uses an even vertex outputted by TC, so we need * to multiply the expected value by 2. */ expected_value.x *= 2.0f; expected_value.y *= 2.0f; expected_value.z *= 2.0f; expected_value.w *= 2.0f; if (de::abs(data.x - expected_value.x) > epsilon || de::abs(data.y - expected_value.y) > epsilon || de::abs(data.z - expected_value.z) > epsilon || de::abs(data.w - expected_value.w) > epsilon) { m_testCtx.getLog() << tcu::TestLog::Message << "Invalid tc_patch_data value found at index [" << n_vertex << "];" " expected:" " [" << expected_value.x << ", " << expected_value.y << ", " << expected_value.z << ", " << expected_value.w << "], found:" " [" << data.x << ", " << data.y << ", " << data.z << ", " << data.w << "]." << tcu::TestLog::EndMessage; TCU_FAIL("Invalid tc_patch_data value found"); } } } /* for (all runs) */ /* All done */ m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); return STOP; } /** Constructor * * @param context Test context **/ TessellationShaderTCTEgl_in::TessellationShaderTCTEgl_in(Context& context, const ExtParameters& extParams) : TestCaseBase(context, extParams, "gl_in", "Verifies values of gl_in[] in a tessellation evaluation shader " "are taken from output variables of a tessellation control shader" "if one is present.") , m_bo_id(0) , m_fs_id(0) , m_po_id(0) , m_tcs_id(0) , m_tes_id(0) , m_vao_id(0) , m_vs_id(0) { /* Left blank on purpose */ } /** Deinitializes all ES objects created for the test. */ void TessellationShaderTCTEgl_in::deinit() { /** Call base class' deinit() function */ TestCaseBase::deinit(); if (!m_is_tessellation_shader_supported) { return; } const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Revert TF buffer object bindings */ gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* buffer */); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* index */, 0 /* buffer */); /* Reset GL_PATCH_VERTICES_EXT value to the default setting */ gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, 3); /* Disable GL_RASTERIZER_DISCARD mdoe */ gl.disable(GL_RASTERIZER_DISCARD); /* Unbind vertex array object */ gl.bindVertexArray(0); /* Release all objects we might've created */ 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_po_id != 0) { gl.deleteProgram(m_po_id); m_po_id = 0; } if (m_tcs_id != 0) { gl.deleteShader(m_tcs_id); m_tcs_id = 0; } if (m_tes_id != 0) { gl.deleteShader(m_tes_id); m_tes_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; } } /** Initializes all ES objects that will be used for the test. */ void TessellationShaderTCTEgl_in::initTest() { /* The test requires EXT_tessellation_shader */ if (!m_is_tessellation_shader_supported) { throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED); } /* Generate a program object we will later configure */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Initialize vertex array object */ gl.genVertexArrays(1, &m_vao_id); GLU_EXPECT_NO_ERROR(gl.getError(), "Could not generate vertex array object"); gl.bindVertexArray(m_vao_id); GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding vertex array object!"); /* Create program object */ m_po_id = gl.createProgram(); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() failed"); /* Generate shader objects the test will use */ m_fs_id = gl.createShader(GL_FRAGMENT_SHADER); m_tcs_id = gl.createShader(m_glExtTokens.TESS_CONTROL_SHADER); m_tes_id = gl.createShader(m_glExtTokens.TESS_EVALUATION_SHADER); m_vs_id = gl.createShader(GL_VERTEX_SHADER); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() failed"); /* Configure fragment shader */ 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 object"); /* Configure tessellation control shader */ const char* tc_body = "${VERSION}\n" "\n" /* Required EXT_tessellation_shader functionality */ "${TESSELLATION_SHADER_REQUIRE}\n" "\n" "layout (vertices = 1) out;\n" "\n" "out float out_float[];\n" "out int out_int[];\n" "out ivec3 out_ivec3[];\n" "out mat2 out_mat2[];\n" "out uint out_uint[];\n" "out uvec2 out_uvec2[];\n" "out vec4 out_vec4[];\n" "\n" "out struct\n" "{\n" " int test1;\n" " float test2;\n" "} out_struct[];\n" /* Body */ "void main()\n" "{\n" " gl_out [gl_InvocationID].gl_Position = vec4(5.0, 6.0, 7.0, 8.0);\n" " gl_TessLevelOuter[0] = 1.0;\n" " gl_TessLevelOuter[1] = 1.0;\n" "\n" " out_float[gl_InvocationID] = 22.0;\n" " out_int [gl_InvocationID] = 23;\n" " out_ivec3[gl_InvocationID] = ivec3(24, 25, 26);\n" " out_mat2 [gl_InvocationID] = mat2(vec2(27.0, 28.0), vec2(29.0, 30.0) );\n" " out_uint [gl_InvocationID] = 31u;\n" " out_uvec2[gl_InvocationID] = uvec2(32, 33);\n" " out_vec4 [gl_InvocationID] = vec4(34.0, 35.0, 36.0, 37.0);\n" "\n" " out_struct[gl_InvocationID].test1 = 38;\n" " out_struct[gl_InvocationID].test2 = 39.0;\n" "}\n"; shaderSourceSpecialized(m_tcs_id, 1 /* count */, &tc_body); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for tessellation control shader object"); /* Configure tessellation evaluation shader */ const char* te_body = "${VERSION}\n" "\n" "${TESSELLATION_SHADER_REQUIRE}\n" "\n" "layout (isolines, point_mode) in;\n" "\n" "in float out_float[];\n" "in int out_int[];\n" "in ivec3 out_ivec3[];\n" "in mat2 out_mat2[];\n" "in uint out_uint[];\n" "in uvec2 out_uvec2[];\n" "in vec4 out_vec4[];\n" "in struct\n" "{\n" " int test1;\n" " float test2;\n" "} out_struct[];\n" "\n" "out float result_float;\n" "flat out int result_int;\n" "flat out ivec3 result_ivec3;\n" "out mat2 result_mat2;\n" "flat out int result_struct_test1;\n" "out float result_struct_test2;\n" "flat out uint result_uint;\n" "flat out uvec2 result_uvec2;\n" "out vec4 result_vec4;\n" "\n" "void main()\n" "{\n" " gl_Position = gl_in[0].gl_Position;\n" "\n" " result_float = out_float [0];\n" " result_int = out_int [0];\n" " result_ivec3 = out_ivec3 [0];\n" " result_mat2 = out_mat2 [0];\n" " result_struct_test1 = out_struct[0].test1;\n" " result_struct_test2 = out_struct[0].test2;\n" " result_uint = out_uint [0];\n" " result_uvec2 = out_uvec2 [0];\n" " result_vec4 = out_vec4 [0];\n" "}\n"; shaderSourceSpecialized(m_tes_id, 1 /* count */, &te_body); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for tessellation evaluation shader object"); /* Configure vertex shader */ const char* vs_body = "${VERSION}\n" "\n" "${SHADER_IO_BLOCKS_ENABLE}\n" "\n" "out float out_float;\n" "flat out int out_int;\n" "flat out ivec3 out_ivec3;\n" "out mat2 out_mat2;\n" "flat out uint out_uint;\n" "flat out uvec2 out_uvec2;\n" "out vec4 out_vec4;\n" "\n" "flat out struct\n" "{\n" " int test1;\n" " float test2;\n" "} out_struct;\n" "\n" "void main()\n" "{\n" " gl_Position = vec4(1.0, 2.0, 3.0, 4.0);\n" "\n" " out_float = 1.0;\n" " out_int = 2;\n" " out_ivec3 = ivec3(3, 4, 5);\n" " out_mat2 = mat2(vec2(6.0, 7.0), vec2(8.0, 9.0) );\n" " out_uint = 10u;\n" " out_uvec2 = uvec2(11u, 12u);\n" " out_vec4 = vec4(12.0, 13.0, 14.0, 15.0);\n" " out_struct.test1 = 20;\n" " out_struct.test2 = 21.0;\n" "}\n"; shaderSourceSpecialized(m_vs_id, 1 /* count */, &vs_body); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for vertex shader object"); /* Compile all shaders of our interest */ const glw::GLuint shaders[] = { m_fs_id, m_tcs_id, m_tes_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::GLint compile_status = GL_FALSE; glw::GLuint shader = shaders[n_shader]; gl.compileShader(shader); GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader() call failed"); gl.getShaderiv(shader, GL_COMPILE_STATUS, &compile_status); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv() call failed"); if (compile_status != GL_TRUE) { const char* src[] = { fs_body, tc_body, te_body, vs_body }; m_testCtx.getLog() << tcu::TestLog::Message << "Compilation of shader object at index " << n_shader << " failed.\n" << "Info log:\n" << getCompilationInfoLog(shader) << "Shader:\n" << src[n_shader] << tcu::TestLog::EndMessage; TCU_FAIL("Shader compilation failed"); } } /* for (all shaders) */ /* Attach the shaders to the test program object, set up XFB and then link the program */ glw::GLint link_status = GL_FALSE; glw::GLint n_xfb_varyings = 0; const glw::GLchar** xfb_varyings = NULL; glw::GLint xfb_size = 0; getXFBProperties(&xfb_varyings, &n_xfb_varyings, &xfb_size); gl.transformFeedbackVaryings(m_po_id, n_xfb_varyings, xfb_varyings, GL_INTERLEAVED_ATTRIBS); GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings() call failed"); gl.attachShader(m_po_id, m_fs_id); gl.attachShader(m_po_id, m_tcs_id); gl.attachShader(m_po_id, m_tes_id); gl.attachShader(m_po_id, m_vs_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader() call(s) failed."); gl.linkProgram(m_po_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glLinkProgram() call failed."); gl.getProgramiv(m_po_id, GL_LINK_STATUS, &link_status); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv() call failed."); if (link_status != GL_TRUE) { TCU_FAIL("Program linking failed"); } /* Generate and set up a buffer object we will use to hold XFBed data. */ gl.genBuffers(1, &m_bo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers() call failed"); gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_bo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call failed"); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* index */, m_bo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase() call failed"); gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, xfb_size, NULL /* data */, GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() call failed"); /* We're good to execute the test! */ } /** Retrieves XFB-specific properties that are used in various locations of * this test implementation. * * @param out_names Deref will be used to store location of an array keeping * names of varyings that should be used for TF. Can be NULL, * in which case nothing will be stored under *out_names. * @param out_n_names Deref will be used to store number of strings the @param * out_names array holds. Can be NULL, in which case nothing * will be stored under *out_n_names. * @param out_xfb_size Deref will be used to store amount of bytes needed to hold * all data generated by a draw call used by this test. Can be * NULL, in which case nothing will be stored under *out_xfb_size. **/ void TessellationShaderTCTEgl_in::getXFBProperties(const glw::GLchar*** out_names, glw::GLint* out_n_names, glw::GLint* out_xfb_size) { static const glw::GLchar* xfb_varyings[] = { "result_float", "result_int", "result_ivec3", "result_mat2", "result_struct_test1", "result_struct_test2", "result_uint", "result_uvec2", "result_vec4", "gl_Position" }; static const unsigned int xfb_size = (sizeof(float) + /* result_float */ sizeof(int) + /* result_int */ sizeof(int) * 3 + /* result_ivec3 */ sizeof(float) * 4 + /* result_mat2 */ sizeof(int) + /* result_struct_test1 */ sizeof(float) + /* result_struct_test2 */ sizeof(int) + /* result_uint */ sizeof(int) * 2 + /* result_uvec2 */ sizeof(float) * 4 + /* result_vec4 */ sizeof(float) * 4) * /* gl_Position */ 2; /* two points will be generated by tessellation */ static const unsigned int n_xfb_varyings = sizeof(xfb_varyings) / sizeof(xfb_varyings[0]); if (out_names != NULL) { *out_names = xfb_varyings; } if (out_n_names != NULL) { *out_n_names = n_xfb_varyings; } if (out_xfb_size != NULL) { /* NOTE: Tessellator is expected to generate two points for the purpose of * this test, which is why we need to multiply the amount of bytes store * in xfb_size by two. */ *out_xfb_size = xfb_size * 2; } } /** 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 TessellationShaderTCTEgl_in::iterate(void) { /* Initialize ES test objects */ initTest(); /* Our program object takes a single vertex per patch */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, 1); GLU_EXPECT_NO_ERROR(gl.getError(), "glPatchParameteriEXT() call failed"); /* Render the geometry. We're only interested in XFB data, not the visual outcome, * so disable rasterization before we fire a draw call. */ gl.useProgram(m_po_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() call failed"); gl.enable(GL_RASTERIZER_DISCARD); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnable(GL_RASTERIZER_DISCARD) call failed"); gl.beginTransformFeedback(GL_POINTS); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback(GL_POINTS) call failed"); { gl.drawArrays(m_glExtTokens.PATCHES, 0 /* first */, 1 /* count */); GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays() call failed"); } gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback() call failed"); /* Download the data we stored with TF */ glw::GLint n_xfb_names = 0; void* rendered_data = NULL; const glw::GLchar** xfb_names = NULL; glw::GLint xfb_size = 0; getXFBProperties(&xfb_names, &n_xfb_names, &xfb_size); rendered_data = gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* offset */, xfb_size, GL_MAP_READ_BIT); GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange() call failed."); /* Move through the result buffer and make sure the values we retrieved are valid. * Note that two points will be generated by the tessellator, so run the checks * twice. */ typedef enum { XFB_VARYING_TYPE_FLOAT, XFB_VARYING_TYPE_INT, XFB_VARYING_TYPE_UNKNOWN } _xfb_varying_type; unsigned char* traveller_ptr = (unsigned char*)rendered_data; for (glw::GLint n_point = 0; n_point < 2 /* points */; ++n_point) { for (glw::GLint n_xfb_name = 0; n_xfb_name < n_xfb_names; ++n_xfb_name) { glw::GLfloat expected_value_float[4] = { 0.0f }; glw::GLint expected_value_int[4] = { 0 }; std::string name = xfb_names[n_xfb_name]; unsigned int n_varying_components = 0; _xfb_varying_type varying_type = XFB_VARYING_TYPE_UNKNOWN; if (name.compare("result_float") == 0) { expected_value_float[0] = 22.0f; n_varying_components = 1; varying_type = XFB_VARYING_TYPE_FLOAT; } else if (name.compare("result_int") == 0) { expected_value_int[0] = 23; n_varying_components = 1; varying_type = XFB_VARYING_TYPE_INT; } else if (name.compare("result_ivec3") == 0) { expected_value_int[0] = 24; expected_value_int[1] = 25; expected_value_int[2] = 26; n_varying_components = 3; varying_type = XFB_VARYING_TYPE_INT; } else if (name.compare("result_mat2") == 0) { expected_value_float[0] = 27.0f; expected_value_float[1] = 28.0f; expected_value_float[2] = 29.0f; expected_value_float[3] = 30.0f; n_varying_components = 4; varying_type = XFB_VARYING_TYPE_FLOAT; } else if (name.compare("result_struct_test1") == 0) { expected_value_int[0] = 38; n_varying_components = 1; varying_type = XFB_VARYING_TYPE_INT; } else if (name.compare("result_struct_test2") == 0) { expected_value_float[0] = 39.0f; n_varying_components = 1; varying_type = XFB_VARYING_TYPE_FLOAT; } else if (name.compare("result_uint") == 0) { expected_value_int[0] = 31; n_varying_components = 1; varying_type = XFB_VARYING_TYPE_INT; } else if (name.compare("result_uvec2") == 0) { expected_value_int[0] = 32; expected_value_int[1] = 33; n_varying_components = 2; varying_type = XFB_VARYING_TYPE_INT; } else if (name.compare("result_vec4") == 0) { expected_value_float[0] = 34.0f; expected_value_float[1] = 35.0f; expected_value_float[2] = 36.0f; expected_value_float[3] = 37.0f; n_varying_components = 4; varying_type = XFB_VARYING_TYPE_FLOAT; } else if (name.compare("gl_Position") == 0) { expected_value_float[0] = 5.0f; expected_value_float[1] = 6.0f; expected_value_float[2] = 7.0f; expected_value_float[3] = 8.0f; n_varying_components = 4; varying_type = XFB_VARYING_TYPE_FLOAT; } else { TCU_FAIL("Unrecognized XFB name"); } /* Move through the requested amount of components and perform type-specific * comparison. */ const float epsilon = (float)1e-5; for (unsigned int n_component = 0; n_component < n_varying_components; ++n_component) { switch (varying_type) { case XFB_VARYING_TYPE_FLOAT: { glw::GLfloat* rendered_value = (glw::GLfloat*)traveller_ptr; if (de::abs(*rendered_value - expected_value_float[n_component]) > epsilon) { m_testCtx.getLog() << tcu::TestLog::Message << "Invalid component at index [" << n_component << "] " << "(found:" << *rendered_value << " expected:" << expected_value_float[n_component] << ") for varying [" << name.c_str() << "]" << tcu::TestLog::EndMessage; } traveller_ptr += sizeof(glw::GLfloat); break; } case XFB_VARYING_TYPE_INT: { glw::GLint* rendered_value = (glw::GLint*)traveller_ptr; if (*rendered_value != expected_value_int[n_component]) { m_testCtx.getLog() << tcu::TestLog::Message << "Invalid component at index [" << n_component << "] " << "(found:" << *rendered_value << " expected:" << expected_value_int[n_component] << ") for varying [" << name.c_str() << "]" << tcu::TestLog::EndMessage; TCU_FAIL("Invalid rendered value"); } traveller_ptr += sizeof(glw::GLint); break; } default: { TCU_FAIL("Unrecognized varying type"); } } /* switch(varying_type) */ } /* for (all components) */ } /* for (all XFBed variables) */ } /* for (both points) */ /* 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 **/ TessellationShaderTCTEgl_MaxPatchVertices_Position_PointSize:: TessellationShaderTCTEgl_MaxPatchVertices_Position_PointSize(Context& context, const ExtParameters& extParams) : TestCaseBase(context, extParams, "gl_MaxPatchVertices_Position_PointSize", "Verifies gl_Position and gl_PointSize (if supported) " "are set to correct values in TE stage. Checks if up to " "gl_MaxPatchVertices input block values can be accessed " "from TE stage. Also verifies if TC/TE stage properties " "can be correctly queried for both regular and separate " "program objects.") , m_bo_id(0) , m_gl_max_patch_vertices_value(0) , m_gl_max_tess_gen_level_value(0) , m_utils_ptr(DE_NULL) , m_vao_id(0) { /* Left blank on purpose */ } /** Deinitializes all ES objects created for the test. */ void TessellationShaderTCTEgl_MaxPatchVertices_Position_PointSize::deinit() { /** Call base class' deinit() function */ TestCaseBase::deinit(); if (!m_is_tessellation_shader_supported) { return; } const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Revert TF buffer object bindings */ gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* buffer */); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* index */, 0 /* buffer */); /* Reset GL_PATCH_VERTICES_EXT value to the default setting */ gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, 3); /* Disable GL_RASTERIZER_DISCARD mode */ gl.disable(GL_RASTERIZER_DISCARD); /* Unbind vertex array object */ gl.bindVertexArray(0); /* Release all objects we might've created */ if (m_bo_id != 0) { gl.deleteBuffers(1, &m_bo_id); m_bo_id = 0; } if (m_vao_id != 0) { gl.deleteVertexArrays(1, &m_vao_id); m_vao_id = 0; } if (m_utils_ptr != DE_NULL) { delete m_utils_ptr; m_utils_ptr = DE_NULL; } /* Release all test runs */ for (_runs::iterator it = m_runs.begin(); it != m_runs.end(); ++it) { deinitTestRun(*it); } m_runs.clear(); } /** Deinitializes all ES objects generated for a test run */ void TessellationShaderTCTEgl_MaxPatchVertices_Position_PointSize::deinitTestRun(_run& run) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); if (run.fs_id != 0) { gl.deleteShader(run.fs_id); run.fs_id = 0; } if (run.fs_program_id != 0) { gl.deleteProgram(run.fs_program_id); run.fs_program_id = 0; } if (run.pipeline_object_id != 0) { gl.deleteProgramPipelines(1, &run.pipeline_object_id); run.pipeline_object_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.tc_program_id != 0) { gl.deleteProgram(run.tc_program_id); run.tc_program_id = 0; } if (run.te_id != 0) { gl.deleteShader(run.te_id); run.te_id = 0; } if (run.te_program_id != 0) { gl.deleteProgram(run.te_program_id); run.te_program_id = 0; } if (run.vs_id != 0) { gl.deleteShader(run.vs_id); run.vs_id = 0; } if (run.vs_program_id != 0) { gl.deleteProgram(run.vs_program_id); run.vs_program_id = 0; } } /** Retrieves a minimal fragment shader code to be used for forming program objects * used by the test. * * @return As per description. **/ std::string TessellationShaderTCTEgl_MaxPatchVertices_Position_PointSize::getFragmentShaderCode( bool should_accept_pointsize_data) { // Requires input to match previous stage's output std::stringstream result_code; result_code << "${VERSION}\n" "\n" "${SHADER_IO_BLOCKS_REQUIRE}\n" "\n" "precision highp float;\n" "precision highp int;\n" "out layout (location = 0) vec4 col;\n"; if (should_accept_pointsize_data) { result_code << "in float te_pointsize;\n"; } result_code << "in vec4 te_position;\n" "in vec2 te_value1;\n" "in flat ivec4 te_value2;\n" "\n" "void main()\n" "{\n" " col = vec4(1.0, 1.0, 1.0, 1.0);\n" "}\n"; return result_code.str(); } /** Retrieves tessellation control shader source code, given user-provided arguments. * * @param should_pass_pointsize_data true if Tessellation Control shader should configure * gl_PointSize value. This should be only set to true * if the tested ES implementation reports support of * GL_EXT_tessellation_point_size extension. * @param inner_tess_levels Two FP values defining inner tessellation level values. * Must not be NULL. * @param outer_tess_levels Four FP values defining outer tessellation level values. * Must not be NULL. * * @return As per description. **/ std::string TessellationShaderTCTEgl_MaxPatchVertices_Position_PointSize::getTessellationControlShaderCode( bool should_pass_pointsize_data, const glw::GLfloat* inner_tess_levels, const glw::GLfloat* outer_tess_levels) { std::stringstream result_code; result_code << "${VERSION}\n" "\n" "${TESSELLATION_SHADER_REQUIRE}\n"; if (should_pass_pointsize_data) { result_code << "${TESSELLATION_POINT_SIZE_REQUIRE}\n"; } result_code << "\n" "layout(vertices = " << m_gl_max_patch_vertices_value << ") out;\n" "\n"; if (should_pass_pointsize_data) { result_code << "${IN_PER_VERTEX_DECL_ARRAY_POINT_SIZE}"; result_code << "${OUT_PER_VERTEX_DECL_ARRAY_POINT_SIZE}"; } else { result_code << "${IN_PER_VERTEX_DECL_ARRAY}"; result_code << "${OUT_PER_VERTEX_DECL_ARRAY}"; } result_code << "out OUT_TC\n" "{\n" " vec2 value1;\n" " ivec4 value2;\n" "} result[];\n" "\n" "void main()\n" "{\n"; if (should_pass_pointsize_data) { result_code << " gl_out[gl_InvocationID].gl_PointSize = 1.0 / float(gl_InvocationID + 1);\n"; } result_code << " gl_out[gl_InvocationID].gl_Position = vec4( float(gl_InvocationID * 4 + 0), " "float(gl_InvocationID * 4 + 1),\n" " float(gl_InvocationID * 4 + 2), " "float(gl_InvocationID * 4 + 3));\n" " result[gl_InvocationID].value1 = vec2(1.0 / float(gl_InvocationID + 1), 1.0 / " "float(gl_InvocationID + 2) );\n" " result[gl_InvocationID].value2 = ivec4( gl_InvocationID + 1, " "gl_InvocationID + 2,\n" " gl_InvocationID + 3, " "gl_InvocationID + 4);\n" "\n" " gl_TessLevelInner[0] = float(" << inner_tess_levels[0] << ");\n" " gl_TessLevelInner[1] = float(" << inner_tess_levels[1] << ");\n" " gl_TessLevelOuter[0] = float(" << outer_tess_levels[0] << ");\n" " gl_TessLevelOuter[1] = float(" << outer_tess_levels[1] << ");\n" " gl_TessLevelOuter[2] = float(" << outer_tess_levels[2] << ");\n" " gl_TessLevelOuter[3] = float(" << outer_tess_levels[3] << ");\n" "}\n"; return result_code.str(); } /** Retrieves tessellation evaluation shader source code, given user-provided arguments. * * @param should_pass_pointsize_data true if Tessellation Evaluation shader should set * gl_PointSize to the value set by Tessellation Control * stage. This should be only set to true if the tested * ES implementation reports support of GL_EXT_tessellation_point_size * extension, and TC stage assigns a value to gl_PointSize. * @param primitive_mode Primitive mode to use for the stage. * @param vertex_ordering Vertex ordering to use for the stage. * @param vertex_spacing Vertex spacing to use for the stage. * @param is_point_mode_enabled true to make the TE stage work in point mode, false otherwise. * * @return As per description. **/ std::string TessellationShaderTCTEgl_MaxPatchVertices_Position_PointSize::getTessellationEvaluationShaderCode( bool should_pass_pointsize_data, _tessellation_primitive_mode primitive_mode, _tessellation_shader_vertex_ordering vertex_ordering, _tessellation_shader_vertex_spacing vertex_spacing, bool is_point_mode_enabled) { std::stringstream result_sstream; std::string result; result_sstream << "${VERSION}\n" "\n" "${TESSELLATION_SHADER_REQUIRE}\n"; if (should_pass_pointsize_data) { result_sstream << "${TESSELLATION_POINT_SIZE_REQUIRE}\n"; } result_sstream << "\n" "layout (TESSELLATOR_PRIMITIVE_MODE VERTEX_SPACING_MODE VERTEX_ORDERING POINT_MODE) in;\n" "\n"; if (should_pass_pointsize_data) { result_sstream << "${IN_PER_VERTEX_DECL_ARRAY_POINT_SIZE}"; result_sstream << "${OUT_PER_VERTEX_DECL_POINT_SIZE}"; } else { result_sstream << "${IN_PER_VERTEX_DECL_ARRAY}"; result_sstream << "${OUT_PER_VERTEX_DECL}"; } result_sstream << "in OUT_TC\n" "{\n" " vec2 value1;\n" " ivec4 value2;\n" "} tc_data[];\n" "\n"; if (should_pass_pointsize_data) { result_sstream << "out float te_pointsize;\n"; } result_sstream << "out vec4 te_position;\n" "out vec2 te_value1;\n" "out flat ivec4 te_value2;\n" "\n" "void main()\n" "{\n"; if (should_pass_pointsize_data) { result_sstream << " te_pointsize = 0.0;\n"; } result_sstream << " te_position = vec4 (0.0);\n" " te_value1 = vec2 (0.0);\n" " te_value2 = ivec4(0);\n" "\n" " for (int n = 0; n < " << m_gl_max_patch_vertices_value << "; ++n)\n" " {\n"; if (should_pass_pointsize_data) { result_sstream << " te_pointsize += gl_in[n].gl_PointSize;\n"; } result_sstream << " te_position += gl_in [n].gl_Position;\n" " te_value1 += tc_data[n].value1;\n" " te_value2 += tc_data[n].value2;\n" " }\n" "}\n"; result = result_sstream.str(); /* Replace the tokens */ const char* point_mode_token = "POINT_MODE"; std::size_t point_mode_token_index = std::string::npos; std::string primitive_mode_string = TessellationShaderUtils::getESTokenForPrimitiveMode(primitive_mode); const char* primitive_mode_token = "TESSELLATOR_PRIMITIVE_MODE"; std::size_t primitive_mode_token_index = std::string::npos; std::string vertex_ordering_string; const char* vertex_ordering_token = "VERTEX_ORDERING"; std::size_t vertex_ordering_token_index = std::string::npos; std::string vertex_spacing_mode_string; const char* vertex_spacing_token = "VERTEX_SPACING_MODE"; std::size_t vertex_spacing_token_index = std::string::npos; /* Prepare the vertex ordering token. We need to do this manually, because the default vertex spacing * mode translates to empty string and the shader would fail to compile if we hadn't taken care of the * comma */ if (vertex_ordering == TESSELLATION_SHADER_VERTEX_ORDERING_DEFAULT) { vertex_ordering_string = TessellationShaderUtils::getESTokenForVertexOrderingMode(vertex_ordering); } else { std::stringstream helper_sstream; helper_sstream << ", " << TessellationShaderUtils::getESTokenForVertexOrderingMode(vertex_ordering); vertex_ordering_string = helper_sstream.str(); } /* Do the same for vertex spacing token */ if (vertex_spacing == TESSELLATION_SHADER_VERTEX_SPACING_DEFAULT) { vertex_spacing_mode_string = TessellationShaderUtils::getESTokenForVertexSpacingMode(vertex_spacing); } else { std::stringstream helper_sstream; helper_sstream << ", " << TessellationShaderUtils::getESTokenForVertexSpacingMode(vertex_spacing); vertex_spacing_mode_string = helper_sstream.str(); } /* Primitive mode */ 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); } /* Vertex ordering */ while ((vertex_ordering_token_index = result.find(vertex_ordering_token)) != std::string::npos) { result = result.replace(vertex_ordering_token_index, strlen(vertex_ordering_token), vertex_ordering_string); vertex_ordering_token_index = result.find(vertex_ordering_token); } /* Vertex spacing */ while ((vertex_spacing_token_index = result.find(vertex_spacing_token)) != std::string::npos) { result = result.replace(vertex_spacing_token_index, strlen(vertex_spacing_token), vertex_spacing_mode_string); vertex_spacing_token_index = result.find(vertex_spacing_token); } /* Point mode */ while ((point_mode_token_index = result.find(point_mode_token)) != std::string::npos) { result = result.replace(point_mode_token_index, strlen(point_mode_token), (is_point_mode_enabled) ? ", point_mode" : ""); point_mode_token_index = result.find(point_mode_token); } return result; } /** Retrieves a minimal vertex shader code to be used for forming program objects * used by the test. * * @return As per description. **/ std::string TessellationShaderTCTEgl_MaxPatchVertices_Position_PointSize::getVertexShaderCode( bool should_pass_pointsize_data) { std::stringstream result_sstream; result_sstream << "${VERSION}\n\n"; if (should_pass_pointsize_data) { result_sstream << "${OUT_PER_VERTEX_DECL_POINT_SIZE}"; } else { result_sstream << "${OUT_PER_VERTEX_DECL}"; } result_sstream << "\n" "void main()\n" "{\n" "}\n"; return result_sstream.str(); } /** Initializes all ES objects that will be used for the test. */ void TessellationShaderTCTEgl_MaxPatchVertices_Position_PointSize::initTest() { /* The test requires EXT_tessellation_shader */ if (!m_is_tessellation_shader_supported) { throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED); } /* Retrieve ES entry-points */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Initialize vertex array object */ gl.genVertexArrays(1, &m_vao_id); GLU_EXPECT_NO_ERROR(gl.getError(), "Could not generate vertex array object"); gl.bindVertexArray(m_vao_id); GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding vertex array object!"); /* Generate a buffer object we will use to hold XFB data */ gl.genBuffers(1, &m_bo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers() failed"); /* Configure XFB buffer object bindings */ gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_bo_id); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* index */, m_bo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() / glBindBufferBase() call(s) failed"); /* Retrieve GL_MAX_TESS_GEN_LEVEL_EXT value */ gl.getIntegerv(m_glExtTokens.MAX_TESS_GEN_LEVEL, &m_gl_max_tess_gen_level_value); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() failed for GL_MAX_TESS_GEN_LEVEL_EXT pname"); /* Retrieve GL_MAX_PATCH_VERTICES_EXT value */ gl.getIntegerv(m_glExtTokens.MAX_PATCH_VERTICES, &m_gl_max_patch_vertices_value); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() failed for GL_MAX_PATCH_VERTICES_EXT pname"); /* We only need 1 vertex per input patch */ gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, 1); GLU_EXPECT_NO_ERROR(gl.getError(), "glPatchParameteriEXT() failed for GL_PATCH_VERTICES_EXT pname"); /* Disable rasterization */ gl.enable(GL_RASTERIZER_DISCARD); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnable(GL_RASTERIZER_DISCARD) failed"); /* Spawn utilities class instance */ m_utils_ptr = new TessellationShaderUtils(gl, this); /* Initialize all test iterations */ bool point_mode_enabled_flags[] = { false, true }; const _tessellation_primitive_mode primitive_modes[] = { TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES, TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS, TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES }; const _tessellation_shader_vertex_ordering vertex_ordering_modes[] = { TESSELLATION_SHADER_VERTEX_ORDERING_CCW, TESSELLATION_SHADER_VERTEX_ORDERING_CW, TESSELLATION_SHADER_VERTEX_ORDERING_DEFAULT }; const _tessellation_shader_vertex_spacing vertex_spacing_modes[] = { TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_EVEN, TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD, TESSELLATION_SHADER_VERTEX_SPACING_DEFAULT }; const unsigned int n_point_mode_enabled_flags = sizeof(point_mode_enabled_flags) / sizeof(point_mode_enabled_flags[0]); const unsigned int n_primitive_modes = sizeof(primitive_modes) / sizeof(primitive_modes[0]); const unsigned int n_vertex_ordering_modes = sizeof(vertex_ordering_modes) / sizeof(vertex_ordering_modes[0]); const unsigned int n_vertex_spacing_modes = sizeof(vertex_spacing_modes) / sizeof(vertex_spacing_modes[0]); bool deleteResources = false; for (unsigned int n_primitive_mode = 0; n_primitive_mode < n_primitive_modes; n_primitive_mode++) { _tessellation_primitive_mode primitive_mode = primitive_modes[n_primitive_mode]; _tessellation_levels_set tessellation_levels = TessellationShaderUtils::getTessellationLevelSetForPrimitiveMode( primitive_mode, m_gl_max_tess_gen_level_value, TESSELLATION_LEVEL_SET_FILTER_ALL_LEVELS_USE_THE_SAME_VALUE); for (_tessellation_levels_set_const_iterator tessellation_levels_iterator = tessellation_levels.begin(); tessellation_levels_iterator != tessellation_levels.end(); tessellation_levels_iterator++) { const _tessellation_levels& tess_levels = *tessellation_levels_iterator; for (unsigned int n_vertex_ordering_mode = 0; n_vertex_ordering_mode < n_vertex_ordering_modes; ++n_vertex_ordering_mode) { _tessellation_shader_vertex_ordering vertex_ordering = vertex_ordering_modes[n_vertex_ordering_mode]; for (unsigned int n_vertex_spacing_mode = 0; n_vertex_spacing_mode < n_vertex_spacing_modes; ++n_vertex_spacing_mode) { _tessellation_shader_vertex_spacing vertex_spacing = vertex_spacing_modes[n_vertex_spacing_mode]; for (unsigned int n_point_mode_enabled_flag = 0; n_point_mode_enabled_flag < n_point_mode_enabled_flags; ++n_point_mode_enabled_flag) { bool is_point_mode_enabled = point_mode_enabled_flags[n_point_mode_enabled_flag]; /* Only create gl_PointSize-enabled runs if the implementation supports * GL_EXT_tessellation_point_size extension */ if (!m_is_tessellation_shader_point_size_supported && is_point_mode_enabled) { continue; } /* Execute the test run */ _run run; memcpy(run.inner, tess_levels.inner, sizeof(run.inner)); memcpy(run.outer, tess_levels.outer, sizeof(run.outer)); run.point_mode = is_point_mode_enabled; run.primitive_mode = primitive_mode; run.vertex_ordering = vertex_ordering; run.vertex_spacing = vertex_spacing; initTestRun(run); if (deleteResources) { deinitTestRun(run); } deleteResources = true; /* Store it for further processing */ m_runs.push_back(run); } /* for (all 'point mode' enabled flags) */ } /* for (all vertex spacing modes) */ } /* for (all vertex ordering modes) */ } /* for (all tessellation levels for active primitive mode) */ } /* for (all primitive modes) */ } /** Initializes all ES objects used by the test, captures the tessellation coordinates * and stores them in the descriptor. * Also performs a handful of other minor checks, as described by test specification. * * @param run Run descriptor to operate on. **/ void TessellationShaderTCTEgl_MaxPatchVertices_Position_PointSize::initTestRun(_run& run) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Build shader objects */ run.fs_id = gl.createShader(GL_FRAGMENT_SHADER); run.tc_id = gl.createShader(m_glExtTokens.TESS_CONTROL_SHADER); run.te_id = gl.createShader(m_glExtTokens.TESS_EVALUATION_SHADER); run.vs_id = gl.createShader(GL_VERTEX_SHADER); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() call(s) failed"); /* Generate fragment shader (or stand-alone program) */ std::string fs_code_string = getFragmentShaderCode(run.point_mode); const char* fs_code_raw_ptr = fs_code_string.c_str(); shaderSourceSpecialized(run.fs_id, 1 /* count */, &fs_code_raw_ptr); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() call failed for fragment shader"); /* Generate tessellation control shader (or stand-alone program) */ std::string tc_code_string = getTessellationControlShaderCode(run.point_mode, run.inner, run.outer); const char* tc_code_raw_ptr = tc_code_string.c_str(); shaderSourceSpecialized(run.tc_id, 1 /* count */, &tc_code_raw_ptr); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() call failed for tessellation control shader"); /* Generate tessellation evaluation shader (or stand-alone program) */ std::string te_code_string = getTessellationEvaluationShaderCode( run.point_mode, run.primitive_mode, run.vertex_ordering, run.vertex_spacing, run.point_mode); const char* te_code_raw_ptr = te_code_string.c_str(); shaderSourceSpecialized(run.te_id, 1 /* count */, &te_code_raw_ptr); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() call failed for tessellation evaluation shader"); /* Generate vertex shader (or stand-alone program) */ std::string vs_code_string = getVertexShaderCode(run.point_mode); const char* vs_code_raw_ptr = vs_code_string.c_str(); shaderSourceSpecialized(run.vs_id, 1 /* count */, &vs_code_raw_ptr); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() call failed for vertex shader"); /* Compile all shaders first. Also make sure the shader objects we have * attached are correctly reported. */ const glw::GLuint shaders[] = { run.fs_id, run.tc_id, run.te_id, run.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::GLint compile_status = GL_FALSE; glw::GLint shader = shaders[n_shader]; 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) { m_testCtx.getLog() << tcu::TestLog::Message << "Info log:\n" << getCompilationInfoLog(shader) << "\nShader:\n" << getShaderSource(shader) << tcu::TestLog::EndMessage; TCU_FAIL("Shader compilation failed"); } } /* Run two iterations: * * 1) First, using a program object; * 2) The other one using pipeline objects; */ for (unsigned int n_iteration = 0; n_iteration < 2 /* program / pipeline objects */; ++n_iteration) { bool should_use_program_object = (n_iteration == 0); /* Generate container object(s) first */ if (!should_use_program_object) { gl.genProgramPipelines(1, &run.pipeline_object_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenProgramPipelines() failed"); /* As per test spec, make sure no tessellation stages are defined for * a pipeline object by default */ glw::GLint program_tc_id = 1; glw::GLint program_te_id = 1; gl.getProgramPipelineiv(run.pipeline_object_id, m_glExtTokens.TESS_CONTROL_SHADER, &program_tc_id); gl.getProgramPipelineiv(run.pipeline_object_id, m_glExtTokens.TESS_EVALUATION_SHADER, &program_te_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramPipelineiv() failed"); if (program_tc_id != 0 || program_te_id != 0) { GLU_EXPECT_NO_ERROR(gl.getError(), "A pipeline object returned a non-zero ID of " "a separate program object when asked for TC/TE" " program ID."); } } else { run.po_id = gl.createProgram(); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() failed"); } if (!should_use_program_object) { run.fs_program_id = gl.createProgram(); run.tc_program_id = gl.createProgram(); run.te_program_id = gl.createProgram(); run.vs_program_id = gl.createProgram(); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() call(s) failed"); } /* Link program object(s) (and configure the pipeline object, if necessary) */ const glw::GLuint programs_for_pipeline_iteration[] = { run.fs_program_id, run.tc_program_id, run.te_program_id, run.vs_program_id }; const glw::GLuint programs_for_program_iteration[] = { run.po_id }; const unsigned int n_programs_for_pipeline_iteration = sizeof(programs_for_pipeline_iteration) / sizeof(programs_for_pipeline_iteration[0]); const unsigned int n_programs_for_program_iteration = sizeof(programs_for_program_iteration) / sizeof(programs_for_program_iteration[0]); unsigned int n_programs = 0; const glw::GLuint* programs = DE_NULL; int xfb_pointsize_data_offset = -1; int xfb_position_data_offset = -1; int xfb_value1_data_offset = -1; int xfb_value2_data_offset = -1; int xfb_varyings_size = 0; if (should_use_program_object) { n_programs = n_programs_for_program_iteration; programs = programs_for_program_iteration; } else { n_programs = n_programs_for_pipeline_iteration; programs = programs_for_pipeline_iteration; } /* Attach and verify shader objects */ for (unsigned int n_shader = 0; n_shader < n_shaders; ++n_shader) { glw::GLuint parent_po_id = 0; glw::GLuint shader = shaders[n_shader]; if (should_use_program_object) { gl.attachShader(run.po_id, shader); GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader() failed"); parent_po_id = run.po_id; } else { if (shader == run.fs_id) { gl.attachShader(run.fs_program_id, run.fs_id); parent_po_id = run.fs_program_id; } else if (shader == run.tc_id) { gl.attachShader(run.tc_program_id, run.tc_id); parent_po_id = run.tc_program_id; } else if (shader == run.te_id) { gl.attachShader(run.te_program_id, run.te_id); parent_po_id = run.te_program_id; } else { DE_ASSERT(shader == run.vs_id); gl.attachShader(run.vs_program_id, run.vs_id); parent_po_id = run.vs_program_id; } GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader() failed"); } /* Make sure the shader object we've attached is reported as a part * of the program object. */ unsigned int attached_shaders[n_shaders] = { 0 }; bool has_found_attached_shader = false; glw::GLsizei n_attached_shaders = 0; memset(attached_shaders, 0, sizeof(attached_shaders)); gl.getAttachedShaders(parent_po_id, n_shaders, &n_attached_shaders, attached_shaders); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetAttachedShaders() failed"); for (glw::GLsizei n_attached_shader = 0; n_attached_shader < n_attached_shaders; n_attached_shader++) { if (attached_shaders[n_attached_shader] == shader) { has_found_attached_shader = true; break; } } /* for (all attached shader object IDs) */ if (!has_found_attached_shader) { TCU_FAIL("A shader object that was successfully attached to a program " "object was not reported as one by subsequent glGetAttachedShaders() " "call"); } } /* Set up XFB */ const char* xfb_varyings_w_pointsize[] = { "te_position", "te_value1", "te_value2", "te_pointsize" }; const char* xfb_varyings_wo_pointsize[] = { "te_position", "te_value1", "te_value2", }; const char** xfb_varyings = DE_NULL; unsigned int n_xfb_varyings = 0; if (run.point_mode) { xfb_varyings = xfb_varyings_w_pointsize; n_xfb_varyings = sizeof(xfb_varyings_w_pointsize) / sizeof(xfb_varyings_w_pointsize[0]); xfb_position_data_offset = 0; xfb_value1_data_offset = static_cast(xfb_position_data_offset + sizeof(float) * 4); /* size of te_position */ xfb_value2_data_offset = static_cast(xfb_value1_data_offset + sizeof(float) * 2); /* size of te_value1 */ xfb_pointsize_data_offset = static_cast(xfb_value2_data_offset + sizeof(int) * 4); /* size of te_value2 */ xfb_varyings_size = sizeof(float) * 4 + /* size of te_position */ sizeof(float) * 2 + /* size of te_value1 */ sizeof(int) * 4 + /* size of te_value2 */ sizeof(int); /* size of te_pointsize */ } else { xfb_varyings = xfb_varyings_wo_pointsize; n_xfb_varyings = sizeof(xfb_varyings_wo_pointsize) / sizeof(xfb_varyings_wo_pointsize[0]); xfb_position_data_offset = 0; xfb_value1_data_offset = static_cast(xfb_position_data_offset + sizeof(float) * 4); /* size of te_position */ xfb_value2_data_offset = static_cast(xfb_value1_data_offset + sizeof(float) * 2); /* size of te_value1 */ xfb_varyings_size = sizeof(float) * 4 + /* size of te_position */ sizeof(float) * 2 + /* size of te_value1 */ sizeof(int) * 4; } if (!should_use_program_object) { gl.transformFeedbackVaryings(run.te_program_id, n_xfb_varyings, xfb_varyings, GL_INTERLEAVED_ATTRIBS); GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings() failed"); } else { gl.transformFeedbackVaryings(run.po_id, n_xfb_varyings, xfb_varyings, GL_INTERLEAVED_ATTRIBS); GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings() failed"); } /* Mark all program objects as separable for pipeline run */ if (!should_use_program_object) { for (unsigned int n_program = 0; n_program < n_programs; ++n_program) { glw::GLuint program = programs[n_program]; gl.programParameteri(program, GL_PROGRAM_SEPARABLE, GL_TRUE); GLU_EXPECT_NO_ERROR(gl.getError(), "glProgramParameteri() failed."); } } /* Link the program object(s) */ for (unsigned int n_program = 0; n_program < n_programs; ++n_program) { glw::GLint link_status = GL_FALSE; glw::GLuint program = programs[n_program]; gl.linkProgram(program); GLU_EXPECT_NO_ERROR(gl.getError(), "Program linking failed"); gl.getProgramiv(program, GL_LINK_STATUS, &link_status); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv() failed"); if (link_status != GL_TRUE) { m_testCtx.getLog() << tcu::TestLog::Message << "Info log:\n" << getLinkingInfoLog(program) << tcu::TestLog::EndMessage; TCU_FAIL("Program linking failed"); } /* Make sure glGetProgramiv() reports correct tessellation properties for * the program object we've just linked successfully */ if (program == run.po_id || program == run.tc_program_id || program == run.te_program_id) { glw::GLenum expected_tess_gen_mode_value = GL_NONE; glw::GLenum expected_tess_gen_spacing_value = GL_NONE; glw::GLenum expected_tess_gen_vertex_order_value = GL_NONE; glw::GLint tess_control_output_vertices_value = GL_NONE; glw::GLint tess_gen_mode_value = GL_NONE; glw::GLint tess_gen_point_mode_value = GL_NONE; glw::GLint tess_gen_spacing_value = GL_NONE; glw::GLint tess_gen_vertex_order_value = GL_NONE; switch (run.primitive_mode) { case TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES: expected_tess_gen_mode_value = m_glExtTokens.ISOLINES; break; case TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS: expected_tess_gen_mode_value = m_glExtTokens.QUADS; break; case TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES: expected_tess_gen_mode_value = GL_TRIANGLES; break; default: { /* Unrecognized primitive mode? */ DE_ASSERT(false); } } /* switch (run.primitive_mode) */ switch (run.vertex_spacing) { case TESSELLATION_SHADER_VERTEX_SPACING_DEFAULT: case TESSELLATION_SHADER_VERTEX_SPACING_EQUAL: expected_tess_gen_spacing_value = GL_EQUAL; break; case TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_EVEN: expected_tess_gen_spacing_value = m_glExtTokens.FRACTIONAL_EVEN; break; case TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD: expected_tess_gen_spacing_value = m_glExtTokens.FRACTIONAL_ODD; break; default: { /* Unrecognized vertex spacing mode? */ DE_ASSERT(false); } } /* switch (run.vertex_spacing) */ switch (run.vertex_ordering) { case TESSELLATION_SHADER_VERTEX_ORDERING_DEFAULT: case TESSELLATION_SHADER_VERTEX_ORDERING_CCW: expected_tess_gen_vertex_order_value = GL_CCW; break; case TESSELLATION_SHADER_VERTEX_ORDERING_CW: expected_tess_gen_vertex_order_value = GL_CW; break; default: { /* Unrecognized vertex ordering mode? */ DE_ASSERT(false); } } /* switch (run.vertex_ordering) */ if (program == run.po_id || program == run.tc_program_id) { gl.getProgramiv(program, m_glExtTokens.TESS_CONTROL_OUTPUT_VERTICES, &tess_control_output_vertices_value); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv() failed for GL_TESS_CONTROL_OUTPUT_VERTICES_EXT pname"); if (tess_control_output_vertices_value != m_gl_max_patch_vertices_value) { TCU_FAIL( "Invalid value returned by glGetProgramiv() for GL_TESS_CONTROL_OUTPUT_VERTICES_EXT query"); } } if (program == run.po_id || program == run.te_program_id) { gl.getProgramiv(program, m_glExtTokens.TESS_GEN_MODE, &tess_gen_mode_value); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv() failed for GL_TESS_GEN_MODE_EXT pname"); if ((glw::GLuint)tess_gen_mode_value != expected_tess_gen_mode_value) { TCU_FAIL("Invalid value returned by glGetProgramiv() for GL_TESS_GEN_MODE_EXT query"); } } if (program == run.po_id || program == run.te_program_id) { gl.getProgramiv(program, m_glExtTokens.TESS_GEN_SPACING, &tess_gen_spacing_value); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv() failed for GL_TESS_GEN_SPACING_EXT pname"); if ((glw::GLuint)tess_gen_spacing_value != expected_tess_gen_spacing_value) { TCU_FAIL("Invalid value returned by glGetProgramiv() for GL_TESS_GEN_SPACING_EXT query"); } } if (program == run.po_id || program == run.te_program_id) { gl.getProgramiv(program, m_glExtTokens.TESS_GEN_VERTEX_ORDER, &tess_gen_vertex_order_value); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv() failed for GL_TESS_GEN_VERTEX_ORDER_EXT pname"); if ((glw::GLuint)tess_gen_vertex_order_value != expected_tess_gen_vertex_order_value) { TCU_FAIL("Invalid value returned by glGetProgramiv() for GL_TESS_GEN_VERTEX_ORDER_EXT query"); } } if (program == run.po_id || program == run.te_program_id) { gl.getProgramiv(program, m_glExtTokens.TESS_GEN_POINT_MODE, &tess_gen_point_mode_value); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv() failed for GL_TESS_GEN_POINT_MODE_EXT pname"); if (tess_gen_point_mode_value != ((run.point_mode) ? GL_TRUE : GL_FALSE)) { TCU_FAIL("Invalid value returned by glGetProgramiv() for GL_TESS_GEN_POINT_MODE_EXT query"); } } } /* if (program == run.po_id || program == run.tc_program_id || program == run.te_program_id) */ } /* for (all considered program objects) */ if (!should_use_program_object) { /* Attach all stages to the pipeline object */ gl.useProgramStages(run.pipeline_object_id, GL_FRAGMENT_SHADER_BIT, run.fs_program_id); gl.useProgramStages(run.pipeline_object_id, m_glExtTokens.TESS_CONTROL_SHADER_BIT, run.tc_program_id); gl.useProgramStages(run.pipeline_object_id, m_glExtTokens.TESS_EVALUATION_SHADER_BIT, run.te_program_id); gl.useProgramStages(run.pipeline_object_id, GL_VERTEX_SHADER_BIT, run.vs_program_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgramStages() call(s) failed"); /* Make sure the pipeline object validates correctly */ glw::GLint validate_status = GL_FALSE; gl.validateProgramPipeline(run.pipeline_object_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glValidateProgramPipeline() call failed"); gl.getProgramPipelineiv(run.pipeline_object_id, GL_VALIDATE_STATUS, &validate_status); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramPipelineiv() call failed"); if (validate_status != GL_TRUE) { m_testCtx.getLog() << tcu::TestLog::Message << "Info log:\n" << getPipelineInfoLog(run.pipeline_object_id) << "\n\nVertex Shader:\n" << vs_code_raw_ptr << "\n\nTessellation Control Shader:\n" << tc_code_raw_ptr << "\n\nTessellation Evaluation Shader:\n" << te_code_raw_ptr << "\n\nFragment Shader:\n" << fs_code_raw_ptr << tcu::TestLog::EndMessage; TCU_FAIL("Pipeline object was found to be invalid"); } } /* Determine how many vertices are going to be generated by the tessellator * for particular tessellation configuration. */ unsigned int n_vertices_generated = m_utils_ptr->getAmountOfVerticesGeneratedByTessellator( run.primitive_mode, run.inner, run.outer, run.vertex_spacing, run.point_mode); /* Allocate enough space to hold the result XFB data */ const unsigned int bo_size = xfb_varyings_size * n_vertices_generated; gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, bo_size, DE_NULL /* data */, GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() call failed"); /* Use the pipeline or program object and render the data */ glw::GLenum tf_mode = TessellationShaderUtils::getTFModeForPrimitiveMode(run.primitive_mode, run.point_mode); if (should_use_program_object) { gl.bindProgramPipeline(0); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindProgramPipeline() call failed"); gl.useProgram(run.po_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() call failed"); } else { gl.bindProgramPipeline(run.pipeline_object_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindProgramPipeline() call failed"); gl.useProgram(0); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() call failed"); } gl.beginTransformFeedback(tf_mode); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback() call failed"); { gl.drawArrays(m_glExtTokens.PATCHES, 0 /* first */, 1 /* count */); GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays() call failed"); } gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback() call failed"); /* Map the buffer object contents into process space */ const char* xfb_data = (const char*)gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, /* offset */ bo_size, GL_MAP_READ_BIT); GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange() call failed"); /* Iterate through all vertices and extract all captured data. To reduce amount * of time necessary to verify the generated data, only store *unique* values. */ for (unsigned int n_vertex = 0; n_vertex < n_vertices_generated; ++n_vertex) { if (xfb_pointsize_data_offset != -1) { const float* data_ptr = (const float*)(xfb_data + xfb_varyings_size * n_vertex + xfb_pointsize_data_offset); if (std::find(run.result_pointsize_data.begin(), run.result_pointsize_data.end(), *data_ptr) == run.result_pointsize_data.end()) { run.result_pointsize_data.push_back(*data_ptr); } } if (xfb_position_data_offset != -1) { const float* data_ptr = (const float*)(xfb_data + xfb_varyings_size * n_vertex + xfb_position_data_offset); _vec4 new_item = _vec4(data_ptr[0], data_ptr[1], data_ptr[2], data_ptr[3]); if (std::find(run.result_position_data.begin(), run.result_position_data.end(), new_item) == run.result_position_data.end()) { run.result_position_data.push_back(new_item); } } if (xfb_value1_data_offset != -1) { const float* data_ptr = (const float*)(xfb_data + xfb_varyings_size * n_vertex + xfb_value1_data_offset); _vec2 new_item = _vec2(data_ptr[0], data_ptr[1]); if (std::find(run.result_value1_data.begin(), run.result_value1_data.end(), new_item) == run.result_value1_data.end()) { run.result_value1_data.push_back(new_item); } } if (xfb_value2_data_offset != -1) { const int* data_ptr = (const int*)(xfb_data + xfb_varyings_size * n_vertex + xfb_value2_data_offset); _ivec4 new_item = _ivec4(data_ptr[0], data_ptr[1], data_ptr[2], data_ptr[3]); if (std::find(run.result_value2_data.begin(), run.result_value2_data.end(), new_item) == run.result_value2_data.end()) { run.result_value2_data.push_back(new_item); } } } /* for (all result tessellation coordinates) */ /* Good to unmap the buffer object at this point */ gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER); GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer() call failed"); } /* for (two iterations) */ } /** Executes the test. * * Sets the test result to QP_TEST_RESULT_FAIL if the test failed, QP_TEST_RESULT_PASS otherwise. * * Note the function throws exception should an error occur! * * @return STOP if the test has finished, CONTINUE to indicate iterate() should be called once again. **/ tcu::TestNode::IterateResult TessellationShaderTCTEgl_MaxPatchVertices_Position_PointSize::iterate(void) { /* Initialize ES test objects */ initTest(); /* Calculate reference values that should be generated for all runs */ float reference_result_pointsize(0); _vec4 reference_result_position(0, 0, 0, 0); _vec2 reference_result_value1(0, 0); _ivec4 reference_result_value2(0, 0, 0, 0); const float epsilon = (float)1e-5; for (glw::GLint n_invocation = 0; n_invocation < m_gl_max_patch_vertices_value; ++n_invocation) { /* As per TC and TE shaders */ reference_result_pointsize += 1.0f / static_cast(n_invocation + 1); reference_result_position.x += static_cast(n_invocation * 4 + 0); reference_result_position.y += static_cast(n_invocation * 4 + 1); reference_result_position.z += static_cast(n_invocation * 4 + 2); reference_result_position.w += static_cast(n_invocation * 4 + 3); reference_result_value1.x += 1.0f / static_cast(n_invocation + 1); reference_result_value1.y += 1.0f / static_cast(n_invocation + 2); reference_result_value2.x += (n_invocation + 1); reference_result_value2.y += (n_invocation + 2); reference_result_value2.z += (n_invocation + 3); reference_result_value2.w += (n_invocation + 4); } /* Iterate through test runs and analyse the result data */ for (_runs_const_iterator run_iterator = m_runs.begin(); run_iterator != m_runs.end(); run_iterator++) { const _run& run = *run_iterator; /* For the very first run, make sure that the type of tessellation shader objects * is reported correctly for both program and pipeline object cases. */ if (run_iterator == m_runs.begin()) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); glw::GLint shader_type_tc = GL_NONE; glw::GLint shader_type_te = GL_NONE; /* Program objects first */ gl.getShaderiv(run.tc_id, GL_SHADER_TYPE, &shader_type_tc); gl.getShaderiv(run.te_id, GL_SHADER_TYPE, &shader_type_te); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv() call(s) failed"); if ((glw::GLenum)shader_type_tc != m_glExtTokens.TESS_CONTROL_SHADER) { TCU_FAIL("Invalid shader type reported by glGetShaderiv() for a tessellation control shader"); } if ((glw::GLenum)shader_type_te != m_glExtTokens.TESS_EVALUATION_SHADER) { TCU_FAIL("Invalid shader type reported by glGetShaderiv() for a tessellation evaluation shader"); } /* Let's query the pipeline object now */ glw::GLint shader_id_tc = 0; glw::GLint shader_id_te = 0; gl.getProgramPipelineiv(run.pipeline_object_id, m_glExtTokens.TESS_CONTROL_SHADER, &shader_id_tc); gl.getProgramPipelineiv(run.pipeline_object_id, m_glExtTokens.TESS_EVALUATION_SHADER, &shader_id_te); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramPipelineiv() failed for GL_TESS_CONTROL_SHADER_EXT / " "GL_TESS_EVALUATION_SHADER_EXT enum(s)"); if ((glw::GLuint)shader_id_tc != run.tc_program_id) { TCU_FAIL("Invalid separate program object ID reported for Tessellation Control stage"); } if ((glw::GLuint)shader_id_te != run.te_program_id) { TCU_FAIL("Invalid separate program object ID reported for Tessellation Evaluation stage"); } } if ((run.point_mode && run.result_pointsize_data.size() != 1) || (run.point_mode && de::abs(run.result_pointsize_data[0] - reference_result_pointsize) > epsilon)) { /* It is a test bug if result_pointsize_data.size() == 0 */ DE_ASSERT(run.result_pointsize_data.size() > 0); m_testCtx.getLog() << tcu::TestLog::Message << "Tessellation Evaluation stage set gl_PointSize value to " << run.result_pointsize_data[0] << " instead of expected value " << reference_result_pointsize << tcu::TestLog::EndMessage; TCU_FAIL("Invalid gl_PointSize data exposed in TE stage"); } if (run.result_position_data.size() != 1 || run.result_position_data[0] != reference_result_position) { /* It is a test bug if result_position_data.size() == 0 */ DE_ASSERT(run.result_position_data.size() > 0); m_testCtx.getLog() << tcu::TestLog::Message << "Tessellation Evaluation stage set gl_Position to " << " (" << run.result_position_data[0].x << ", " << run.result_position_data[0].y << ", " << run.result_position_data[0].z << ", " << run.result_position_data[0].w << " ) instead of expected value" " (" << reference_result_position.x << ", " << reference_result_position.y << ", " << reference_result_position.z << ", " << reference_result_position.w << ")" << tcu::TestLog::EndMessage; TCU_FAIL("Invalid gl_Position data exposed in TE stage"); } if (run.result_value1_data.size() != 1 || de::abs(run.result_value1_data[0].x - reference_result_value1.x) > epsilon || de::abs(run.result_value1_data[0].y - reference_result_value1.y) > epsilon) { /* It is a test bug if result_value1_data.size() == 0 */ DE_ASSERT(run.result_value1_data.size() > 0); m_testCtx.getLog() << tcu::TestLog::Message << "Tessellation Evaluation stage set te_value1 to " << " (" << run.result_value1_data[0].x << ", " << run.result_value1_data[0].y << " ) instead of expected value" " (" << reference_result_value1.x << ", " << reference_result_value1.y << ")" << tcu::TestLog::EndMessage; TCU_FAIL("Invalid gl_Position data exposed in TE stage"); } if (run.result_value2_data.size() != 1 || run.result_value2_data[0] != reference_result_value2) { /* It is a test bug if result_value2_data.size() == 0 */ DE_ASSERT(run.result_value2_data.size() > 0); m_testCtx.getLog() << tcu::TestLog::Message << "Tessellation Evaluation stage set te_value2 to " << " (" << run.result_value2_data[0].x << ", " << run.result_value2_data[0].y << ", " << run.result_value2_data[0].z << ", " << run.result_value2_data[0].w << " ) instead of expected value" " (" << reference_result_value2.x << ", " << reference_result_value2.y << ", " << reference_result_value2.z << ", " << reference_result_value2.w << ")" << tcu::TestLog::EndMessage; TCU_FAIL("Invalid value2 data saved in TE stage"); } } /* for (all runs) */ /* All done */ m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); return STOP; } /** Constructor * * @param context Test context **/ TessellationShaderTCTEgl_TessLevel::TessellationShaderTCTEgl_TessLevel(Context& context, const ExtParameters& extParams) : TestCaseBase(context, extParams, "gl_tessLevel", "Verifies gl_TessLevelOuter and gl_TessLevelInner patch variable " "values in a tessellation evaluation shader are valid and correspond" "to values configured in a tessellation control shader (should one be " "present) or to the default values, as set with glPatchParameterfv() calls") , m_gl_max_tess_gen_level_value(0) , m_bo_id(0) , m_vao_id(0) { /* Left blank on purpose */ } /** Deinitializes all ES objects created for the test. */ void TessellationShaderTCTEgl_TessLevel::deinit() { /** Call base class' deinit() function */ TestCaseBase::deinit(); if (!m_is_tessellation_shader_supported) { return; } const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Reset TF buffer object bindings */ gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* buffer */); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* index */, 0 /* buffer */); /* Reset GL_PATCH_VERTICES_EXT value to the default setting */ gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, 3); if (!glu::isContextTypeES(m_context.getRenderContext().getType())) { /* Revert GL_PATCH_DEFAULT_INNER_LEVEL and GL_PATCH_DEFAULT_OUTER_LEVEL pname * values to the default settings */ const float default_levels[] = { 1.0f, 1.0f, 1.0f, 1.0f }; gl.patchParameterfv(GL_PATCH_DEFAULT_INNER_LEVEL, default_levels); gl.patchParameterfv(GL_PATCH_DEFAULT_OUTER_LEVEL, default_levels); } /* Disable GL_RASTERIZER_DISCARD mode */ gl.disable(GL_RASTERIZER_DISCARD); /* Unbind vertex array object */ gl.bindVertexArray(0); /* Release all objects we might've created */ if (m_bo_id != 0) { gl.deleteBuffers(1, &m_bo_id); m_bo_id = 0; } if (m_vao_id != 0) { gl.deleteVertexArrays(1, &m_vao_id); m_vao_id = 0; } for (_tests::iterator it = m_tests.begin(); it != m_tests.end(); ++it) { deinitTestDescriptor(&*it); } m_tests.clear(); } /** Deinitializes ES objects created for particular test pass. * * @param test_ptr Test run descriptor. Must not be NULL. * **/ void TessellationShaderTCTEgl_TessLevel::deinitTestDescriptor(_test_descriptor* test_ptr) { /* Call base class' deinit() */ TestCaseBase::deinit(); /* Release all objects */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); if (test_ptr->fs_id != 0) { gl.deleteShader(test_ptr->fs_id); test_ptr->fs_id = 0; } if (test_ptr->po_id != 0) { gl.deleteProgram(test_ptr->po_id); test_ptr->po_id = 0; } if (test_ptr->tcs_id != 0) { gl.deleteShader(test_ptr->tcs_id); test_ptr->tcs_id = 0; } if (test_ptr->tes_id != 0) { gl.deleteShader(test_ptr->tes_id); test_ptr->tes_id = 0; } if (test_ptr->vs_id != 0) { gl.deleteShader(test_ptr->vs_id); test_ptr->vs_id = 0; } } /** Initializes all ES objects that will be used for the test. */ void TessellationShaderTCTEgl_TessLevel::initTest() { /* The test requires EXT_tessellation_shader */ if (!m_is_tessellation_shader_supported) { throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED); } /* Retrieve GL_MAX_TESS_GEN_LEVEL_EXT value before we carry on */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Initialize vertex array object */ gl.genVertexArrays(1, &m_vao_id); GLU_EXPECT_NO_ERROR(gl.getError(), "Could not generate vertex array object"); gl.bindVertexArray(m_vao_id); GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding vertex array object!"); /* Retrieve gen level */ gl.getIntegerv(m_glExtTokens.MAX_TESS_GEN_LEVEL, &m_gl_max_tess_gen_level_value); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() for GL_MAX_TESS_GEN_LEVEL_EXT pname failed"); /* Initialize test descriptors */ _test_descriptor test_tcs_tes_equal; _test_descriptor test_tcs_tes_fe; _test_descriptor test_tcs_tes_fo; _test_descriptor test_tes_equal; _test_descriptor test_tes_fe; _test_descriptor test_tes_fo; initTestDescriptor(TESSELLATION_TEST_TYPE_TCS_TES, &test_tcs_tes_equal, TESSELLATION_SHADER_VERTEX_SPACING_EQUAL); initTestDescriptor(TESSELLATION_TEST_TYPE_TCS_TES, &test_tcs_tes_fe, TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_EVEN); initTestDescriptor(TESSELLATION_TEST_TYPE_TCS_TES, &test_tcs_tes_fo, TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD); if (!glu::isContextTypeES(m_context.getRenderContext().getType())) { initTestDescriptor(TESSELLATION_TEST_TYPE_TES, &test_tes_equal, TESSELLATION_SHADER_VERTEX_SPACING_EQUAL); initTestDescriptor(TESSELLATION_TEST_TYPE_TES, &test_tes_fe, TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_EVEN); initTestDescriptor(TESSELLATION_TEST_TYPE_TES, &test_tes_fo, TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD); } m_tests.push_back(test_tcs_tes_equal); m_tests.push_back(test_tcs_tes_fe); m_tests.push_back(test_tcs_tes_fo); if (!glu::isContextTypeES(m_context.getRenderContext().getType())) { m_tests.push_back(test_tes_equal); m_tests.push_back(test_tes_fe); m_tests.push_back(test_tes_fo); } /* Generate and set up a buffer object we will use to hold XFBed data. * * NOTE: We do not set the buffer object's storage here because its size * is iteration-specific. **/ gl.genBuffers(1, &m_bo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers() call failed"); gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_bo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call failed"); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* index */, m_bo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase() call failed"); /* We're good to execute the test! */ } /** Initializes ES objects for a particular tess pass. * * @param test_type Determines test type to be used for initialization. * TEST_TYPE_TCS_TES will use both TC and TE stages, * TEST_TYPE_TES will assume only TE stage should be used. * @param out_test_ptr Deref will be used to store object data. Must not be NULL. * @param vertex_spacing_mode Vertex spacing mode to use for the TE stage. * **/ void TessellationShaderTCTEgl_TessLevel::initTestDescriptor(_tessellation_test_type test_type, _test_descriptor* out_test_ptr, _tessellation_shader_vertex_spacing vertex_spacing_mode) { out_test_ptr->type = test_type; out_test_ptr->vertex_spacing = vertex_spacing_mode; /* Generate a program object we will later configure */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); out_test_ptr->po_id = gl.createProgram(); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() failed"); /* Generate shader objects the test will use */ out_test_ptr->fs_id = gl.createShader(GL_FRAGMENT_SHADER); out_test_ptr->vs_id = gl.createShader(GL_VERTEX_SHADER); if (test_type == TESSELLATION_TEST_TYPE_TCS_TES || test_type == TESSELLATION_TEST_TYPE_TES) { out_test_ptr->tes_id = gl.createShader(m_glExtTokens.TESS_EVALUATION_SHADER); } if (test_type == TESSELLATION_TEST_TYPE_TCS_TES) { out_test_ptr->tcs_id = gl.createShader(m_glExtTokens.TESS_CONTROL_SHADER); } GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() failed"); /* Configure fragment shader */ const char* fs_body = "${VERSION}\n" "\n" "void main()\n" "{\n" "}\n"; shaderSourceSpecialized(out_test_ptr->fs_id, 1 /* count */, &fs_body); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for fragment shader object"); /* Configure tessellation control shader */ const char* tc_body = "${VERSION}\n" "\n" /* Required EXT_tessellation_shader functionality */ "${TESSELLATION_SHADER_REQUIRE}\n" "\n" "layout (vertices = 4) out;\n" "\n" "uniform vec2 inner_tess_levels;\n" "uniform vec4 outer_tess_levels;\n" "\n" "void main()\n" "{\n" " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" " if (gl_InvocationID == 0) {\n" " gl_TessLevelInner[0] = inner_tess_levels[0];\n" " gl_TessLevelInner[1] = inner_tess_levels[1];\n" " gl_TessLevelOuter[0] = outer_tess_levels[0];\n" " gl_TessLevelOuter[1] = outer_tess_levels[1];\n" " gl_TessLevelOuter[2] = outer_tess_levels[2];\n" " gl_TessLevelOuter[3] = outer_tess_levels[3];\n" " }\n" "}\n"; if (test_type == TESSELLATION_TEST_TYPE_TCS_TES) { shaderSourceSpecialized(out_test_ptr->tcs_id, 1 /* count */, &tc_body); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for tessellation control shader object"); } /* Configure tessellation evaluation shader */ const char* te_body = "${VERSION}\n" "\n" "${TESSELLATION_SHADER_REQUIRE}\n" "\n" "layout (quads, point_mode, VERTEX_SPACING_MODE) in;\n" "\n" "out vec2 result_tess_level_inner;\n" "out vec4 result_tess_level_outer;\n" "\n" "void main()\n" "{\n" " vec4 p1 = mix(gl_in[0].gl_Position, gl_in[1].gl_Position, gl_TessCoord.x);\n" " vec4 p2 = mix(gl_in[2].gl_Position,gl_in[3].gl_Position,gl_TessCoord.x);\n" " gl_Position = mix(p1, p2, gl_TessCoord.y);\n" "\n" " result_tess_level_inner = vec2(gl_TessLevelInner[0],\n" " gl_TessLevelInner[1]);\n" " result_tess_level_outer = vec4(gl_TessLevelOuter[0],\n" " gl_TessLevelOuter[1],\n" " gl_TessLevelOuter[2],\n" " gl_TessLevelOuter[3]);\n" "}\n"; if (test_type == TESSELLATION_TEST_TYPE_TCS_TES || test_type == TESSELLATION_TEST_TYPE_TES) { /* Replace VERTEX_SPACING_MODE with the mode provided by the caller */ std::stringstream te_body_stringstream; std::string te_body_string; const std::string token = "VERTEX_SPACING_MODE"; std::size_t token_index; std::string vertex_spacing_string = TessellationShaderUtils::getESTokenForVertexSpacingMode(vertex_spacing_mode); te_body_stringstream << te_body; te_body_string = te_body_stringstream.str(); token_index = te_body_string.find(token); while (token_index != std::string::npos) { te_body_string = te_body_string.replace(token_index, token.length(), vertex_spacing_string.c_str()); token_index = te_body_string.find(token); } /* Set the shader source */ const char* te_body_string_raw = te_body_string.c_str(); shaderSourceSpecialized(out_test_ptr->tes_id, 1 /* count */, &te_body_string_raw); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for tessellation evaluation shader object"); } /* Configure vertex shader */ const char* vs_body = "${VERSION}\n" "\n" "void main()\n" "{\n" " gl_Position = vec4(1.0, 2.0, 3.0, 4.0);\n" "}\n"; shaderSourceSpecialized(out_test_ptr->vs_id, 1 /* count */, &vs_body); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for vertex shader object"); /* Compile all shaders of our interest */ const glw::GLuint shaders[] = { out_test_ptr->fs_id, out_test_ptr->tcs_id, out_test_ptr->tes_id, out_test_ptr->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::GLint compile_status = GL_FALSE; glw::GLuint shader = shaders[n_shader]; if (shader != 0) { gl.compileShader(shader); GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader() call failed"); gl.getShaderiv(shader, GL_COMPILE_STATUS, &compile_status); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv() call failed"); if (compile_status != GL_TRUE) { m_testCtx.getLog() << tcu::TestLog::Message << "Compilation of shader object at index " << n_shader << " failed." << tcu::TestLog::EndMessage; TCU_FAIL("Shader compilation failed"); } } /* if (shader != 0) */ } /* for (all shaders) */ /* Attach the shaders to the test program object, set up XFB and then link the program */ glw::GLint link_status = GL_FALSE; const char* varyings[] = { "result_tess_level_inner", "result_tess_level_outer" }; const unsigned int n_varyings = sizeof(varyings) / sizeof(varyings[0]); gl.transformFeedbackVaryings(out_test_ptr->po_id, n_varyings, varyings, GL_INTERLEAVED_ATTRIBS); GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings() call failed"); gl.attachShader(out_test_ptr->po_id, out_test_ptr->fs_id); gl.attachShader(out_test_ptr->po_id, out_test_ptr->vs_id); if (out_test_ptr->tcs_id != 0) { gl.attachShader(out_test_ptr->po_id, out_test_ptr->tcs_id); } if (out_test_ptr->tes_id != 0) { gl.attachShader(out_test_ptr->po_id, out_test_ptr->tes_id); } GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader() call(s) failed."); gl.linkProgram(out_test_ptr->po_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glLinkProgram() call failed."); gl.getProgramiv(out_test_ptr->po_id, GL_LINK_STATUS, &link_status); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv() call failed."); if (link_status != GL_TRUE) { TCU_FAIL("Program linking failed"); } /* Retrieve uniform locations */ out_test_ptr->inner_tess_levels_uniform_location = gl.getUniformLocation(out_test_ptr->po_id, "inner_tess_levels"); out_test_ptr->outer_tess_levels_uniform_location = gl.getUniformLocation(out_test_ptr->po_id, "outer_tess_levels"); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetUniformLocation() call(s) failed"); if (test_type == TESSELLATION_TEST_TYPE_TCS_TES) { DE_ASSERT(out_test_ptr->inner_tess_levels_uniform_location != -1); DE_ASSERT(out_test_ptr->outer_tess_levels_uniform_location != -1); } } /** 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 TessellationShaderTCTEgl_TessLevel::iterate(void) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Initialize ES test objects */ initTest(); /* Our program object takes a single quad per patch */ gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, 4); GLU_EXPECT_NO_ERROR(gl.getError(), "glPatchParameteriEXT() call failed"); /* Prepare for rendering */ gl.enable(GL_RASTERIZER_DISCARD); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnable(GL_RASTERIZER_DISCARD) call failed."); /* We will iterate through all added tests. */ for (_tests_const_iterator test_iterator = m_tests.begin(); test_iterator != m_tests.end(); ++test_iterator) { /* Iterate through a few different inner/outer tessellation level combinations */ glw::GLfloat tessellation_level_combinations[] = { /* inner[0] */ /* inner[1] */ /* outer[0] */ /* outer[1] */ /* outer[2] */ /* outer[3] */ 1.1f, 1.4f, 2.7f, 3.1f, 4.4f, 5.7f, 64.2f, 32.5f, 16.8f, 8.2f, 4.5f, 2.8f, 3.3f, 6.6f, 9.9f, 12.3f, 15.6f, 18.9f }; const unsigned int n_tessellation_level_combinations = sizeof(tessellation_level_combinations) / sizeof(tessellation_level_combinations[0]) / 6; /* 2 inner + 4 outer levels */ for (unsigned int n_combination = 0; n_combination < n_tessellation_level_combinations; ++n_combination) { glw::GLfloat inner_tess_level[] = { tessellation_level_combinations[n_combination * 6 + 0], tessellation_level_combinations[n_combination * 6 + 1] }; glw::GLfloat outer_tess_level[] = { tessellation_level_combinations[n_combination * 6 + 2], tessellation_level_combinations[n_combination * 6 + 3], tessellation_level_combinations[n_combination * 6 + 4], tessellation_level_combinations[n_combination * 6 + 5] }; TessellationShaderUtils tessUtils(gl, this); const unsigned int n_rendered_vertices = tessUtils.getAmountOfVerticesGeneratedByTessellator( TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS, inner_tess_level, outer_tess_level, test_iterator->vertex_spacing, true); /* is_point_mode_enabled */ /* Test type determines how the tessellation levels should be set. */ gl.useProgram(test_iterator->po_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() call failed"); switch (test_iterator->type) { case TESSELLATION_TEST_TYPE_TCS_TES: { gl.uniform2fv(test_iterator->inner_tess_levels_uniform_location, 1, /* count */ inner_tess_level); GLU_EXPECT_NO_ERROR(gl.getError(), "glUniform2fv() call failed"); gl.uniform4fv(test_iterator->outer_tess_levels_uniform_location, 1, /* count */ outer_tess_level); GLU_EXPECT_NO_ERROR(gl.getError(), "glUniform4fv() call failed"); break; } case TESSELLATION_TEST_TYPE_TES: { if (!glu::isContextTypeES(m_context.getRenderContext().getType())) { gl.patchParameterfv(GL_PATCH_DEFAULT_INNER_LEVEL, inner_tess_level); GLU_EXPECT_NO_ERROR(gl.getError(), "glPatchParameterfv() call failed for" " GL_PATCH_DEFAULT_INNER_LEVEL pname"); gl.patchParameterfv(GL_PATCH_DEFAULT_OUTER_LEVEL, outer_tess_level); GLU_EXPECT_NO_ERROR(gl.getError(), "glPatchParameterfv() call failed for" " GL_PATCH_DEFAULT_OUTER_LEVEL pname"); } break; } default: { TCU_FAIL("Unrecognized test type"); } } /* switch (test_iterator->type) */ /* Set up storage properties for the buffer object, to which XFBed data will be * written. */ const unsigned int n_bytes_needed = static_cast(n_rendered_vertices * (2 /* vec2 */ + 4 /* vec4 */) * sizeof(float)); gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, n_bytes_needed, NULL /* data */, GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() call failed"); /* Render the test geometry */ gl.beginTransformFeedback(GL_POINTS); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback() call failed"); { /* A single vertex will do, since we configured GL_PATCH_VERTICES_EXT to be 1 */ gl.drawArrays(GL_PATCHES_EXT, 0 /* first */, 4 /* count */); GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays() call failed"); } gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback() call failed"); /* Now that the BO is filled with data, map it so we can check the storage's contents */ const float* mapped_data_ptr = (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."); /* Verify the contents. For each result vertex, inner/outer tessellation levels should * be unchanged. */ const float epsilon = (float)1e-5; const unsigned int n_result_points = static_cast(n_bytes_needed / sizeof(float) / (2 /* vec2 */ + 4 /* vec4 */)); for (unsigned int n_point = 0; n_point < n_result_points; ++n_point) { const float* point_data_ptr = mapped_data_ptr + (2 /* vec2 */ + 4 /* vec4 */) * n_point; if (de::abs(point_data_ptr[2] - outer_tess_level[0]) > epsilon || de::abs(point_data_ptr[3] - outer_tess_level[1]) > epsilon) { std::string vertex_spacing_mode_string = TessellationShaderUtils::getESTokenForVertexSpacingMode(test_iterator->vertex_spacing); m_testCtx.getLog() << tcu::TestLog::Message << "Invalid inner/outer tessellation level used in TE stage;" << " expected outer:(" << outer_tess_level[0] << ", " << outer_tess_level[1] << ") " << " rendered outer:(" << point_data_ptr[2] << ", " << point_data_ptr[3] << ")" << " vertex spacing mode: " << vertex_spacing_mode_string.c_str() << tcu::TestLog::EndMessage; TCU_FAIL("Invalid inner/outer tessellation level used in TE stage"); } } /* for (all points) */ /* All done - unmap the storage */ gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER); GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer() call failed."); } /* for (all tess level combinations) */ } /* for (all tests) */ /* All done */ m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); return STOP; } /** Constructor * * @param context Test context **/ TessellationShaderTCTEgl_PatchVerticesIn::TessellationShaderTCTEgl_PatchVerticesIn(Context& context, const ExtParameters& extParams) : TestCaseBase(context, extParams, "gl_PatchVerticesIn", "Verifies gl_PatchVerticesIn size is valid in a tessellation" " evaluation shader and corresponds to the value configured in" " a tessellation control shader (should one be present) or to" " the default value, as set with glPatchParameteriEXT() call") , m_gl_max_patch_vertices_value(0) , m_bo_id(0) , m_vao_id(0) { /* Left blank on purpose */ } /** Deinitializes all ES objects created for the test. */ void TessellationShaderTCTEgl_PatchVerticesIn::deinit() { /** Call base class' deinit() function */ TestCaseBase::deinit(); if (!m_is_tessellation_shader_supported) { return; } const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Reset 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); /* Reset GL_PATCH_VERTICES_EXT to the default setting */ gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, 3); /* Unbind vertex array object */ gl.bindVertexArray(0); /* Release all objects we might've created */ if (m_bo_id != 0) { gl.deleteBuffers(1, &m_bo_id); m_bo_id = 0; } if (m_vao_id != 0) { gl.deleteVertexArrays(1, &m_vao_id); m_vao_id = 0; } for (_tests::iterator it = m_tests.begin(); it != m_tests.end(); ++it) { deinitTestDescriptor(&*it); } m_tests.clear(); } /** Deinitializes ES objects created for particular test pass. * * @param test_ptr Test run descriptor. Must not be NULL. * **/ void TessellationShaderTCTEgl_PatchVerticesIn::deinitTestDescriptor(_test_descriptor* test_ptr) { /* Call base class' deinit() */ TestCaseBase::deinit(); /* Release all objects */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); if (test_ptr->fs_id != 0) { gl.deleteShader(test_ptr->fs_id); test_ptr->fs_id = 0; } if (test_ptr->po_id != 0) { gl.deleteProgram(test_ptr->po_id); test_ptr->po_id = 0; } if (test_ptr->tcs_id != 0) { gl.deleteShader(test_ptr->tcs_id); test_ptr->tcs_id = 0; } if (test_ptr->tes_id != 0) { gl.deleteShader(test_ptr->tes_id); test_ptr->tes_id = 0; } if (test_ptr->vs_id != 0) { gl.deleteShader(test_ptr->vs_id); test_ptr->vs_id = 0; } } /** Initializes all ES objects that will be used for the test. */ void TessellationShaderTCTEgl_PatchVerticesIn::initTest() { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* The test requires EXT_tessellation_shader */ 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!"); /* Retrieve GL_MAX_PATCH_VERTICES_EXT value before we carry on */ gl.getIntegerv(m_glExtTokens.MAX_PATCH_VERTICES, &m_gl_max_patch_vertices_value); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() for GL_MAX_PATCH_VERTICES_EXT pname failed"); /* Initialize test descriptors. * * Make sure the values we use are multiples of 4 - this is because we're using isolines in the * tessellation stage, and in order to have the requested amount of line segments generated, we need * to use a multiply of 4 vertices per patch */ glw::GLint n_half_max_patch_vertices_mul_4 = m_gl_max_patch_vertices_value / 2; glw::GLint n_max_patch_vertices_mul_4 = m_gl_max_patch_vertices_value; if ((n_half_max_patch_vertices_mul_4 % 4) != 0) { /* Round to nearest mul-of-4 integer */ n_half_max_patch_vertices_mul_4 += (4 - (m_gl_max_patch_vertices_value / 2) % 4); } if ((n_max_patch_vertices_mul_4 % 4) != 0) { /* Round to previous nearest mul-of-4 integer */ n_max_patch_vertices_mul_4 -= (m_gl_max_patch_vertices_value % 4); } _test_descriptor test_tcs_tes_4; _test_descriptor test_tcs_tes_half_max_patch_vertices_mul_4; _test_descriptor test_tcs_tes_max_patch_vertices_mul_4; _test_descriptor test_tes_4; _test_descriptor test_tes_half_max_patch_vertices_mul_4; _test_descriptor test_tes_max_patch_vertices_mul_4; initTestDescriptor(TESSELLATION_TEST_TYPE_TCS_TES, &test_tcs_tes_4, 4); initTestDescriptor(TESSELLATION_TEST_TYPE_TCS_TES, &test_tcs_tes_half_max_patch_vertices_mul_4, n_half_max_patch_vertices_mul_4); initTestDescriptor(TESSELLATION_TEST_TYPE_TCS_TES, &test_tcs_tes_max_patch_vertices_mul_4, n_max_patch_vertices_mul_4); if (!glu::isContextTypeES(m_context.getRenderContext().getType())) { initTestDescriptor(TESSELLATION_TEST_TYPE_TES, &test_tes_4, 4); initTestDescriptor(TESSELLATION_TEST_TYPE_TES, &test_tes_half_max_patch_vertices_mul_4, n_half_max_patch_vertices_mul_4); initTestDescriptor(TESSELLATION_TEST_TYPE_TES, &test_tes_max_patch_vertices_mul_4, n_max_patch_vertices_mul_4); } m_tests.push_back(test_tcs_tes_4); m_tests.push_back(test_tcs_tes_half_max_patch_vertices_mul_4); m_tests.push_back(test_tcs_tes_max_patch_vertices_mul_4); if (!glu::isContextTypeES(m_context.getRenderContext().getType())) { m_tests.push_back(test_tes_4); m_tests.push_back(test_tes_half_max_patch_vertices_mul_4); m_tests.push_back(test_tes_max_patch_vertices_mul_4); } /* Generate and set up a buffer object we will use to hold XFBed data. * * NOTE: We do not set the buffer object's storage here because its size * is iteration-specific. **/ gl.genBuffers(1, &m_bo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers() call failed"); gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_bo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call failed"); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* index */, m_bo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase() call failed"); /* We're good to execute the test! */ } /** Initializes ES objects for a particular tess pass. * * @param test_type Determines test type to be used for initialization. * TEST_TYPE_TCS_TES will use both TC and TE stages, * TEST_TYPE_TES will assume only TE stage should be used. * @param out_test_ptr Deref will be used to store object data. Must not be NULL. * @param input_patch_size Tells how many vertices should be used per patch for hte * result program object. **/ void TessellationShaderTCTEgl_PatchVerticesIn::initTestDescriptor(_tessellation_test_type test_type, _test_descriptor* out_test_ptr, unsigned int input_patch_size) { out_test_ptr->input_patch_size = input_patch_size; out_test_ptr->type = test_type; /* Generate a program object we will later configure */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); out_test_ptr->po_id = gl.createProgram(); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() failed"); /* Generate shader objects the test will use */ out_test_ptr->fs_id = gl.createShader(GL_FRAGMENT_SHADER); out_test_ptr->vs_id = gl.createShader(GL_VERTEX_SHADER); if (test_type == TESSELLATION_TEST_TYPE_TCS_TES || test_type == TESSELLATION_TEST_TYPE_TES) { out_test_ptr->tes_id = gl.createShader(m_glExtTokens.TESS_EVALUATION_SHADER); } if (test_type == TESSELLATION_TEST_TYPE_TCS_TES) { out_test_ptr->tcs_id = gl.createShader(m_glExtTokens.TESS_CONTROL_SHADER); } GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() failed"); /* Configure fragment shader */ const char* fs_body = "${VERSION}\n" "\n" "void main()\n" "{\n" "}\n"; shaderSourceSpecialized(out_test_ptr->fs_id, 1 /* count */, &fs_body); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for fragment shader object"); /* Configure tessellation control shader */ const char* tc_body = "${VERSION}\n" "\n" /* Required EXT_tessellation_shader functionality */ "${TESSELLATION_SHADER_REQUIRE}\n" "\n" "layout (vertices = VERTICES_TOKEN) out;\n" "\n" "void main()\n" "{\n" " gl_out [gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" " gl_TessLevelOuter[0] = 1.0;\n" " gl_TessLevelOuter[1] = 1.0;\n" "}\n"; if (test_type == TESSELLATION_TEST_TYPE_TCS_TES) { const char* result_body = NULL; std::string tc_body_string = tc_body; std::size_t token_index = -1; const char* token_string = "VERTICES_TOKEN"; std::stringstream vertices_stringstream; std::string vertices_string; vertices_stringstream << input_patch_size; vertices_string = vertices_stringstream.str(); while ((token_index = tc_body_string.find(token_string)) != std::string::npos) { tc_body_string = tc_body_string.replace(token_index, strlen(token_string), vertices_string); } result_body = tc_body_string.c_str(); shaderSourceSpecialized(out_test_ptr->tcs_id, 1 /* count */, &result_body); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for tessellation control shader object"); } /* Configure tessellation evaluation shader */ const char* te_body = "${VERSION}\n" "\n" "${TESSELLATION_SHADER_REQUIRE}\n" "\n" "layout (isolines, point_mode) in;\n" "\n" "flat out int result_PatchVerticesIn;\n" "\n" "void main()\n" "{\n" " gl_Position = gl_in[0].gl_Position;\n" "\n" " result_PatchVerticesIn = gl_PatchVerticesIn;\n" "}\n"; shaderSourceSpecialized(out_test_ptr->tes_id, 1 /* count */, &te_body); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for tessellation evaluation shader object"); /* Configure vertex shader */ const char* vs_body = "${VERSION}\n" "\n" "void main()\n" "{\n" " gl_Position = vec4(float(gl_VertexID), 2.0, 3.0, 4.0);\n" "}\n"; shaderSourceSpecialized(out_test_ptr->vs_id, 1 /* count */, &vs_body); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for vertex shader object"); /* Compile all shaders of our interest */ const glw::GLuint shaders[] = { out_test_ptr->fs_id, out_test_ptr->tcs_id, out_test_ptr->tes_id, out_test_ptr->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::GLint compile_status = GL_FALSE; glw::GLuint shader = shaders[n_shader]; if (shader != 0) { gl.compileShader(shader); GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader() call failed"); gl.getShaderiv(shader, GL_COMPILE_STATUS, &compile_status); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv() call failed"); if (compile_status != GL_TRUE) { m_testCtx.getLog() << tcu::TestLog::Message << "Compilation of shader object at index " << n_shader << " failed." << tcu::TestLog::EndMessage; TCU_FAIL("Shader compilation failed"); } } /* if (shader != 0) */ } /* for (all shaders) */ /* Attach the shaders to the test program object, set up XFB and then link the program */ glw::GLint link_status = GL_FALSE; const char* varyings[] = { "result_PatchVerticesIn", }; const unsigned int n_varyings = sizeof(varyings) / sizeof(varyings[0]); gl.transformFeedbackVaryings(out_test_ptr->po_id, n_varyings, varyings, GL_INTERLEAVED_ATTRIBS); GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings() call failed"); gl.attachShader(out_test_ptr->po_id, out_test_ptr->fs_id); gl.attachShader(out_test_ptr->po_id, out_test_ptr->vs_id); if (out_test_ptr->tcs_id != 0) { gl.attachShader(out_test_ptr->po_id, out_test_ptr->tcs_id); } if (out_test_ptr->tes_id != 0) { gl.attachShader(out_test_ptr->po_id, out_test_ptr->tes_id); } GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader() call(s) failed."); gl.linkProgram(out_test_ptr->po_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glLinkProgram() call failed."); gl.getProgramiv(out_test_ptr->po_id, GL_LINK_STATUS, &link_status); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv() call failed."); if (link_status != GL_TRUE) { TCU_FAIL("Program linking failed"); } } /** 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 TessellationShaderTCTEgl_PatchVerticesIn::iterate(void) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Initialize ES test objects */ initTest(); /* Prepare for rendering */ gl.enable(GL_RASTERIZER_DISCARD); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnable(GL_RASTERIZER_DISCARD) call failed."); /* We will iterate through all added tests. */ for (_tests_const_iterator test_iterator = m_tests.begin(); test_iterator != m_tests.end(); ++test_iterator) { /* Activate test-specific program object first. */ gl.useProgram(test_iterator->po_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() call failed"); /* Test type tells determines how the tessellation levels should be set. * We don't need to do anything specific if TCS+TES are in, but if no * TCS is present, we need to configure default amount of input patch-vertices * to the test-specific value. */ glw::GLint n_patch_vertices = 0; switch (test_iterator->type) { case TESSELLATION_TEST_TYPE_TCS_TES: { /* We're using isolines mode which requires at least 4 input vertices per patch */ gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, 4); GLU_EXPECT_NO_ERROR(gl.getError(), "glPatchParameteriEXT() for GL_PATCH_VERTICES_EXT failed."); n_patch_vertices = 4; break; } case TESSELLATION_TEST_TYPE_TES: { gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, test_iterator->input_patch_size); GLU_EXPECT_NO_ERROR(gl.getError(), "glPatchParameteriEXT() call failed"); n_patch_vertices = test_iterator->input_patch_size; break; } default: { TCU_FAIL("Unrecognized test type"); } } /* switch (test_iterator->type) */ /* Set up storage properties for the buffer object, to which XFBed data will be * written. **/ const unsigned int n_bytes_needed = sizeof(int) * 2; /* the tessellator will output two vertices */ gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, n_bytes_needed, NULL /* data */, GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() call failed"); /* Render the test geometry */ gl.beginTransformFeedback(GL_POINTS); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback() call failed"); { /* Pass a single patch only */ gl.drawArrays(m_glExtTokens.PATCHES, 0 /* first */, n_patch_vertices); GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays() call failed"); } gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback() call failed"); /* Now that the BO is filled with data, map it so we can check the storage's contents */ const int* mapped_data_ptr = (const int*)gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, /* offset */ n_bytes_needed, GL_MAP_READ_BIT); GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange() call failed."); /* Verify the contents. Make sure the value we retrieved is equal to the test-specific * amount of vertices per patch. */ for (unsigned int n_vertex = 0; n_vertex < 2 /* output vertices */; ++n_vertex) { unsigned int te_PatchVerticesInSize = mapped_data_ptr[n_vertex]; if (te_PatchVerticesInSize != test_iterator->input_patch_size) { m_testCtx.getLog() << tcu::TestLog::Message << "Invalid gl_PatchVerticesIn defined for TE stage " << " and result vertex index:" << n_vertex << " expected:" << test_iterator->input_patch_size << " rendered:" << te_PatchVerticesInSize << tcu::TestLog::EndMessage; TCU_FAIL("Invalid gl_PatchVerticesIn size used in TE stage"); } /* if (comparison failed) */ } /* All done - unmap the storage */ gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER); GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer() call failed."); } /* for (all tests) */ /* All done */ m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); return STOP; } } /* namespace glcts */