| /*------------------------------------------------------------------------- |
| * 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 |
| */ /*-------------------------------------------------------------------*/ |
| |
| /** |
| */ /*! |
| * \file esextcDrawElementsBaseVertexTests.cpp |
| * \brief Implements conformance tests for "draw elements base vertex" functionality |
| * for both ES and GL. |
| */ /*-------------------------------------------------------------------*/ |
| |
| #include "esextcDrawElementsBaseVertexTests.hpp" |
| #include "gluContextInfo.hpp" |
| #include "gluDefs.hpp" |
| #include "glwEnums.hpp" |
| #include "glwFunctions.hpp" |
| #include "tcuRenderTarget.hpp" |
| #include "tcuTestLog.hpp" |
| |
| #include <string> |
| #include <vector> |
| |
| namespace glcts |
| { |
| /** Constructor. |
| * |
| * @param context Rendering context |
| * @param name Test name |
| * @param description Test description |
| */ |
| DrawElementsBaseVertexTestBase::DrawElementsBaseVertexTestBase(glcts::Context& context, const ExtParameters& extParams, |
| const char* name, const char* description) |
| : TestCaseBase(context, extParams, name, description) |
| , m_is_draw_elements_base_vertex_supported(false) |
| , m_is_ext_multi_draw_arrays_supported(false) |
| , m_is_geometry_shader_supported(false) |
| , m_is_tessellation_shader_supported(false) |
| , m_is_vertex_attrib_binding_supported(false) |
| , m_bo_id(0) |
| , m_bo_id_2(0) |
| , m_fbo_id(0) |
| , m_fs_id(0) |
| , m_gs_id(0) |
| , m_po_id(0) |
| , m_po_color_attribute_location(-1) |
| , m_po_uses_gs_stage(false) |
| , m_po_uses_tc_te_stages(false) |
| , m_po_uses_vertex_attrib_binding(false) |
| , m_po_vertex_attribute_location(-1) |
| , m_tc_id(0) |
| , m_te_id(0) |
| , m_to_base_id(0) |
| , m_to_ref_id(0) |
| , m_vao_id(0) |
| , m_vs_id(0) |
| , m_bo_negative_data_index_size(-1) |
| , m_bo_negative_data_vertex_size(-1) |
| , m_draw_call_color_offset(DE_NULL) |
| , m_draw_call_index_offset(DE_NULL) |
| , m_draw_call_index2_offset(DE_NULL) |
| , m_draw_call_index3_offset(DE_NULL) |
| , m_draw_call_index4_offset(DE_NULL) |
| , m_draw_call_index5_offset(DE_NULL) |
| , m_draw_call_vertex_offset(DE_NULL) |
| , m_draw_call_vertex2_offset(DE_NULL) |
| , m_to_height(128) |
| , m_to_width(128) |
| , m_to_base_data(DE_NULL) |
| , m_to_ref_data(DE_NULL) |
| { |
| static const glw::GLuint functional_index_data[] = /* used for a number of Functional Tests */ |
| { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 }; |
| static const glw::GLuint functional2_index_data[] = /* used for Functional Test IV */ |
| { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, |
| 30, 31, 32, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; |
| static const glw::GLubyte functional3_index_data[] = /* used for Functional Test IX */ |
| { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; |
| static const glw::GLushort functional4_index_data[] = /* used for Functional Test IX */ |
| { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; |
| static const glw::GLuint functional5_index_data[] = /* used for Functional Test IX */ |
| { 2147483647 + 3u, /* 2^31 + 2 */ |
| 2147483647 + 4u, |
| 2147483647 + 5u, |
| 2147483647 + 6u, |
| 2147483647 + 7u, |
| 2147483647 + 8u, |
| 2147483647 + 9u, |
| 2147483647 + 10u, |
| 2147483647 + 11u, |
| 2147483647 + 12u, |
| 2147483647 + 13u, |
| 2147483647 + 14u, |
| 257, // regular draw call indices for ubyte ref image |
| 258, |
| 259, |
| 260, |
| 261, |
| 262, |
| 263, |
| 264, |
| 265, |
| 266, |
| 267, |
| 268, |
| 65537, // regular draw call indices for ushort ref image |
| 65538, |
| 65539, |
| 65540, |
| 65541, |
| 65542, |
| 65543, |
| 65544, |
| 65545, |
| 65546, |
| 65547, |
| 65548, |
| 0, // regular draw call indices for uint ref image |
| 1, |
| 2, |
| 3, |
| 4, |
| 5, |
| 6, |
| 7, |
| 8, |
| 9, |
| 10, |
| 11 }; |
| static const glw::GLfloat functional_color_data[] = /* used for "vertex attrib binding" Functional Test */ |
| { |
| 0.1f, 0.2f, 0.2f, 0.3f, 0.3f, 0.4f, 0.4f, 0.5f, 0.5f, 0.6f, 0.6f, 0.7f, 0.7f, 0.8f, 0.8f, 0.9f, 0.9f, |
| 1.0f, 1.0f, 0.9f, 0.9f, 0.8f, 0.8f, 0.7f, 0.7f, 0.6f, 0.6f, 0.5f, 0.5f, 0.4f, 0.4f, 0.3f, 0.3f, 0.2f, |
| 0.2f, 0.1f, 0.1f, 0.0f, 0.0f, 0.1f, 0.1f, 0.2f, 0.2f, 0.4f, 0.3f, 0.9f, 0.4f, 0.8f, 0.5f, 1.0f, 0.6f, |
| 0.8f, 0.7f, 0.1f, 0.8f, 0.3f, 0.9f, 0.5f, 1.0f, 0.0f, 0.2f, 0.0f, 0.0f, 0.3f, 0.1f, 1.0f, |
| }; |
| static const glw::GLfloat functional_vertex_data[] = /* used by a number of Functional Tests */ |
| { |
| 0.0f, 0.0f, -0.1f, -0.1f, 0.2f, 0.8f, -0.3f, -0.3f, 0.4f, -0.4f, 0.5f, 0.5f, -0.6f, 0.6f, |
| 0.7f, -0.7f, -0.8f, 0.8f, 0.9f, -0.9f, -1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 1.0f, -0.9f, 0.1f, |
| 0.8f, -0.2f, -0.7f, 0.3f, -0.6f, -0.4f, 0.5f, -0.5f, -0.4f, -0.6f, 0.3f, 0.7f, -0.2f, -0.8f, |
| -0.1f, -0.9f, 0.0f, 0.0f, 0.5f, 0.5f, -0.6f, 0.6f, 0.7f, -0.7f, -0.8f, 0.8f, 0.9f, -0.9f, |
| -1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 1.0f, -0.9f, 0.1f, 0.8f, -0.2f, |
| }; |
| static const glw::GLfloat functional2_vertex_data[] = /* used by a number of Functional Tests */ |
| { |
| -1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.3f, 0.3f, 0.4f, 0.4f, 0.5f, 0.5f, 0.6f, 0.6f, 0.7f, 0.7f, |
| 0.8f, 0.8f, 0.9f, 0.9f, 0.1f, 0.2f, 0.2f, 0.1f, 0.1f, 0.2f, 0.9f, 0.1f, 0.8f, 0.2f, 0.7f, 0.3f, |
| 0.6f, 0.4f, 0.5f, 0.5f, 0.4f, 0.6f, 0.3f, 0.7f, 0.2f, 0.8f, 0.1f, 0.9f, 0.0f, 0.0f, |
| |
| }; |
| static const glw::GLuint negative_index_data[] = { 0, 1, 2 }; |
| static const float negative_vertex_data[] = { -1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 1.0f }; |
| |
| m_bo_functional_data_color = functional_color_data; |
| m_bo_functional_data_color_size = sizeof(functional_color_data); |
| m_bo_functional_data_index = functional_index_data; |
| m_bo_functional_data_index_size = sizeof(functional_index_data); |
| m_bo_functional_data_vertex = functional_vertex_data; |
| m_bo_functional_data_vertex_size = sizeof(functional_vertex_data); |
| m_bo_functional2_data_index = functional2_index_data; |
| m_bo_functional2_data_index_size = sizeof(functional2_index_data); |
| m_bo_functional3_data_index = functional3_index_data; |
| m_bo_functional3_data_index_size = sizeof(functional3_index_data); |
| m_bo_functional4_data_index = functional4_index_data; |
| m_bo_functional4_data_index_size = sizeof(functional4_index_data); |
| m_bo_functional5_data_index = functional5_index_data; |
| m_bo_functional5_data_index_size = sizeof(functional5_index_data); |
| m_bo_functional2_data_vertex = functional2_vertex_data; |
| m_bo_functional2_data_vertex_size = sizeof(functional2_vertex_data); |
| m_bo_negative_data_index = negative_index_data; |
| m_bo_negative_data_index_size = sizeof(negative_index_data); |
| m_bo_negative_data_vertex = negative_vertex_data; |
| m_bo_negative_data_vertex_size = sizeof(negative_vertex_data); |
| } |
| |
| /** Creates & initializes a number of shader objects, assigns user-provided |
| * code to relevant shader objects and compiles them. If all shaders are |
| * compiled successfully, they are later attached to a program object, id |
| * of which is stored in m_po_id. Finally, the program object is linked. |
| * |
| * If the compilation process or the linking process fails for any reason, |
| * the method throws a TestError exception. |
| * |
| * Fragment shader object ID is stored under m_fs_id. |
| * Geometry shader object ID is stored under m_gs_id. |
| * Tessellation control shader object ID is stored under m_tc_id. |
| * Tessellation evaluation shader object ID is stored under m_te_id. |
| * Vertex shader object ID is stored under m_vs_id. |
| * |
| * @param fs_code Code to use for the fragment shader. Must not be NULL. |
| * @param gs_code Code to use for the geometry shader. Can be NULL. |
| * @param tc_code Code to use for the tessellation control shader. Can be NULL. |
| * @param te_code Code to use for the tessellation evaluation shader. Can be NULL. |
| * @param vs_code Code to use for the vertex shader. Must not be NULL. |
| * |
| */ |
| void DrawElementsBaseVertexTestBase::buildProgram(const char* fs_code, const char* vs_code, const char* tc_code, |
| const char* te_code, const char* gs_code) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| /* Create program & shader objects */ |
| m_fs_id = gl.createShader(GL_FRAGMENT_SHADER); |
| m_vs_id = gl.createShader(GL_VERTEX_SHADER); |
| |
| if (tc_code != DE_NULL) |
| { |
| m_tc_id = gl.createShader(GL_TESS_CONTROL_SHADER); |
| } |
| |
| if (te_code != DE_NULL) |
| { |
| m_te_id = gl.createShader(GL_TESS_EVALUATION_SHADER); |
| } |
| |
| if (gs_code != DE_NULL) |
| { |
| m_gs_id = gl.createShader(GL_GEOMETRY_SHADER); |
| } |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() call(s) failed."); |
| |
| m_po_id = gl.createProgram(); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() call failed."); |
| |
| /* Assign source code to the shader objects */ |
| gl.shaderSource(m_fs_id, 1, /* count */ |
| &fs_code, DE_NULL); /* length */ |
| gl.shaderSource(m_vs_id, 1, /* count */ |
| &vs_code, DE_NULL); /* length */ |
| |
| if (m_tc_id != 0) |
| { |
| gl.shaderSource(m_tc_id, 1, /* count */ |
| &tc_code, DE_NULL); /* length */ |
| } |
| |
| if (m_te_id != 0) |
| { |
| gl.shaderSource(m_te_id, 1, /* count */ |
| &te_code, DE_NULL); /* length */ |
| } |
| |
| if (m_gs_id != 0) |
| { |
| gl.shaderSource(m_gs_id, 1, /* count */ |
| &gs_code, DE_NULL); /* length */ |
| } |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() call(s) failed."); |
| |
| /* Try to compile the shaders */ |
| const glw::GLuint so_ids[] = { m_fs_id, m_vs_id, m_tc_id, m_te_id, m_gs_id }; |
| const unsigned int n_so_ids = sizeof(so_ids) / sizeof(so_ids[0]); |
| |
| for (unsigned int n_so_id = 0; n_so_id < n_so_ids; ++n_so_id) |
| { |
| glw::GLint compile_status = GL_FALSE; |
| glw::GLuint so_id = so_ids[n_so_id]; |
| |
| if (so_id != 0) |
| { |
| gl.compileShader(so_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader() call failed"); |
| |
| gl.getShaderiv(so_id, GL_COMPILE_STATUS, &compile_status); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv() call failed."); |
| |
| if (compile_status == GL_FALSE) |
| { |
| TCU_FAIL("Shader compilation failed"); |
| } /* if (compile_status == GL_FALSE) */ |
| } /* if (so_id != 0) */ |
| } /* for (all shader objects) */ |
| |
| /* Attach the shaders to the program object */ |
| gl.attachShader(m_po_id, m_fs_id); |
| gl.attachShader(m_po_id, m_vs_id); |
| |
| if (m_tc_id != 0) |
| { |
| gl.attachShader(m_po_id, m_tc_id); |
| } |
| |
| if (m_te_id != 0) |
| { |
| gl.attachShader(m_po_id, m_te_id); |
| } |
| |
| if (m_gs_id != 0) |
| { |
| gl.attachShader(m_po_id, m_gs_id); |
| } |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader() call failed."); |
| |
| /* Set up TFO */ |
| const glw::GLchar* tf_varyings[] = { "gl_Position" }; |
| |
| gl.transformFeedbackVaryings(m_po_id, 1, /* count */ |
| tf_varyings, GL_SEPARATE_ATTRIBS); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings() call failed."); |
| |
| /* Try to link the program object */ |
| glw::GLint link_status = GL_FALSE; |
| |
| 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_FALSE) |
| { |
| TCU_FAIL("Program linking failed."); |
| } |
| |
| /* Retrieve attribute locations */ |
| m_po_color_attribute_location = |
| gl.getAttribLocation(m_po_id, "in_color"); /* != -1 only for "vertex attrib binding" tests */ |
| m_po_vertex_attribute_location = gl.getAttribLocation(m_po_id, "vertex"); |
| |
| DE_ASSERT(m_po_vertex_attribute_location != -1); |
| } |
| |
| /** Verifies contents of the base & reference textures. This method can work |
| * in two modes: |
| * |
| * a) If @param should_be_equal is true, the method will throw a TestError exception |
| * if the two textures are not a match. |
| * b) If @param should_be_equal is false, the method will throw a TestError exception |
| * if the two extures are a match. |
| * |
| * Furthermore, in order to verify that the basevertex & regular draw calls actually |
| * generated at least one sample, the method verifies that at least one texel in both |
| * of the textures has been modified. If all texels in any of the textures are found |
| * to be (0, 0, 0) (alpha channel is ignored), TestError exception will be generated. |
| * |
| * @param should_be_equal Please see description for more details. |
| **/ |
| void DrawElementsBaseVertexTestBase::compareBaseAndReferenceTextures(bool should_be_equal) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| /* Read contents of both base and reference textures */ |
| gl.framebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_to_base_id, 0); /* level */ |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferTexture2D() call failed."); |
| |
| gl.readPixels(0, /* x */ |
| 0, /* y */ |
| m_to_width, m_to_height, GL_RGBA, GL_UNSIGNED_BYTE, m_to_base_data); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels() call failed."); |
| |
| gl.framebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_to_ref_id, 0); /* level */ |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferTexture2D() call failed."); |
| gl.readPixels(0, /* x */ |
| 0, /* y */ |
| m_to_width, m_to_height, GL_RGBA, GL_UNSIGNED_BYTE, m_to_ref_data); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels() call failed."); |
| |
| /* Both tests should be a match */ |
| const unsigned char* base_data_ptr = m_to_base_data; |
| bool all_base_rgb_texels_zero = true; |
| bool all_reference_rgb_texels_zero = true; |
| const unsigned int n_texels = m_to_width * m_to_height; |
| const unsigned char* ref_data_ptr = m_to_ref_data; |
| bool textures_identical = true; |
| |
| for (unsigned int n_texel = 0; n_texel < n_texels; ++n_texel) |
| { |
| /* Verify something was rendered to one of the render-targets. Note we |
| * omit alpha channel, since the clear color is set to 0xFF by default */ |
| if (base_data_ptr[0] != 0 || base_data_ptr[1] != 0 || base_data_ptr[2] != 0) |
| { |
| all_base_rgb_texels_zero = false; |
| } |
| |
| if (ref_data_ptr[0] != 0 || ref_data_ptr[1] != 0 || ref_data_ptr[2] != 0) |
| { |
| all_reference_rgb_texels_zero = false; |
| } |
| |
| if (base_data_ptr[0] != ref_data_ptr[0] || base_data_ptr[1] != ref_data_ptr[1] || |
| base_data_ptr[2] != ref_data_ptr[2] || base_data_ptr[3] != ref_data_ptr[3]) |
| { |
| if (should_be_equal) |
| { |
| const unsigned int y = n_texel / m_to_width; |
| const unsigned int x = n_texel % m_to_width; |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << "Pixels at (" << x << ", " << y |
| << ") do not match. Found:" |
| << "(" << (unsigned int)base_data_ptr[0] << ", " << (unsigned int)base_data_ptr[1] |
| << ", " << (unsigned int)base_data_ptr[2] << ", " << (unsigned int)base_data_ptr[3] |
| << "), expected:" |
| << "(" << (unsigned int)ref_data_ptr[0] << ", " << (unsigned int)ref_data_ptr[1] |
| << ", " << (unsigned int)ref_data_ptr[2] << ", " << (unsigned int)ref_data_ptr[3] |
| << ")." << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL("Pixel mismatch"); |
| } |
| else |
| { |
| /* Base and reference textures are *not* identical. */ |
| textures_identical = false; |
| } |
| } |
| |
| base_data_ptr += 4; /* components */ |
| ref_data_ptr += 4; /* components */ |
| } /* for (all texels) */ |
| |
| if (all_base_rgb_texels_zero) |
| { |
| TCU_FAIL("Draw call used to render contents of the base texture did not change the texture"); |
| } |
| |
| if (all_reference_rgb_texels_zero) |
| { |
| TCU_FAIL("Draw call used to render contents of the reference texture did not change the texture"); |
| } |
| |
| if (!should_be_equal && textures_identical) |
| { |
| TCU_FAIL("Textures are a match, even though they should not be identical."); |
| } |
| } |
| |
| /** Updates m_draw_call_color_offset, m_draw_call_index*_offset and m_draw_call_vertex*_offset |
| * members with valid values, depending on the input arguments. |
| * |
| * @param use_clientside_index_data true if client-side index data buffer is going to be |
| * used for the test-issued draw calls, false otherwise. |
| * @param use_clientside_vertex_data true if client-side color & vertex data buffer is going |
| * to be used for the test-issued draw calls, false |
| * otherwise. |
| */ |
| void DrawElementsBaseVertexTestBase::computeVBODataOffsets(bool use_clientside_index_data, |
| bool use_clientside_vertex_data) |
| { |
| if (use_clientside_index_data) |
| { |
| m_draw_call_index_offset = m_bo_functional_data_index; |
| m_draw_call_index2_offset = m_bo_functional2_data_index; |
| m_draw_call_index3_offset = m_bo_functional3_data_index; |
| m_draw_call_index4_offset = m_bo_functional4_data_index; |
| m_draw_call_index5_offset = m_bo_functional5_data_index; |
| } |
| else |
| { |
| /* Note that these assignments correspond to how the buffer object storage is constructed. |
| * If you need to update these offsets, don't forget to update the glBufferSubData() calls |
| */ |
| m_draw_call_index_offset = |
| (const glw::GLuint*)(deUintptr)(m_bo_functional_data_vertex_size + m_bo_functional2_data_vertex_size); |
| m_draw_call_index2_offset = (const glw::GLuint*)(deUintptr)( |
| m_bo_functional_data_vertex_size + m_bo_functional2_data_vertex_size + m_bo_functional_data_index_size); |
| m_draw_call_index3_offset = |
| (const glw::GLubyte*)(deUintptr)(m_bo_functional_data_vertex_size + m_bo_functional2_data_vertex_size + |
| m_bo_functional_data_index_size + m_bo_functional2_data_index_size); |
| m_draw_call_index4_offset = (const glw::GLushort*)(deUintptr)( |
| m_bo_functional_data_vertex_size + m_bo_functional2_data_vertex_size + m_bo_functional_data_index_size + |
| m_bo_functional2_data_index_size + m_bo_functional3_data_index_size); |
| m_draw_call_index5_offset = (const glw::GLuint*)(deUintptr)( |
| m_bo_functional_data_vertex_size + m_bo_functional2_data_vertex_size + m_bo_functional_data_index_size + |
| m_bo_functional2_data_index_size + m_bo_functional3_data_index_size + m_bo_functional4_data_index_size); |
| } |
| |
| if (use_clientside_vertex_data) |
| { |
| m_draw_call_color_offset = m_bo_functional_data_color; |
| m_draw_call_vertex_offset = m_bo_functional_data_vertex; |
| m_draw_call_vertex2_offset = m_bo_functional2_data_vertex; |
| } |
| else |
| { |
| /* Note: same observation as above holds. */ |
| m_draw_call_color_offset = (const glw::GLfloat*)(deUintptr)( |
| m_bo_functional_data_vertex_size + m_bo_functional2_data_vertex_size + m_bo_functional_data_index_size + |
| m_bo_functional2_data_index_size + m_bo_functional3_data_index_size + m_bo_functional4_data_index_size + |
| m_bo_functional5_data_index_size); |
| m_draw_call_vertex_offset = DE_NULL; |
| m_draw_call_vertex2_offset = (const glw::GLfloat*)(deUintptr)m_bo_functional_data_vertex_size; |
| } |
| } |
| |
| void DrawElementsBaseVertexTestBase::deinit() |
| { |
| /* TCU_FAIL will skip the per test object de-initialization, we need to |
| * take care of it here |
| */ |
| deinitPerTestObjects(); |
| } |
| |
| /** Deinitializes all ES objects created by the test. */ |
| void DrawElementsBaseVertexTestBase::deinitPerTestObjects() |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| deinitProgramAndShaderObjects(); |
| |
| if (m_bo_id != 0) |
| { |
| gl.deleteBuffers(1, &m_bo_id); |
| |
| m_bo_id = 0; |
| } |
| |
| if (m_bo_id_2 != 0) |
| { |
| gl.deleteBuffers(1, &m_bo_id_2); |
| |
| m_bo_id_2 = 0; |
| } |
| |
| if (m_fbo_id != 0) |
| { |
| gl.deleteFramebuffers(1, &m_fbo_id); |
| |
| m_fbo_id = 0; |
| } |
| |
| if (m_to_base_data != DE_NULL) |
| { |
| delete[] m_to_base_data; |
| |
| m_to_base_data = DE_NULL; |
| } |
| |
| if (m_to_base_id != 0) |
| { |
| gl.deleteTextures(1, &m_to_base_id); |
| |
| m_to_base_id = 0; |
| } |
| |
| if (m_to_ref_data != DE_NULL) |
| { |
| delete[] m_to_ref_data; |
| |
| m_to_ref_data = DE_NULL; |
| } |
| |
| if (m_to_ref_id != 0) |
| { |
| gl.deleteTextures(1, &m_to_ref_id); |
| |
| m_to_ref_id = 0; |
| } |
| |
| if (m_vao_id != 0) |
| { |
| gl.deleteVertexArrays(1, &m_vao_id); |
| |
| m_vao_id = 0; |
| } |
| |
| if (m_vs_id != 0) |
| { |
| gl.deleteShader(m_vs_id); |
| |
| m_vs_id = 0; |
| } |
| |
| /* Restore the default GL_PACK_ALIGNMENT setting */ |
| gl.pixelStorei(GL_PACK_ALIGNMENT, 4); |
| } |
| |
| /** Deinitializes all program & shader objects that may have been initialized |
| * by the test. |
| */ |
| void DrawElementsBaseVertexTestBase::deinitProgramAndShaderObjects() |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| if (m_fs_id != 0) |
| { |
| gl.deleteShader(m_fs_id); |
| |
| m_fs_id = 0; |
| } |
| |
| if (m_gs_id != 0) |
| { |
| gl.deleteShader(m_gs_id); |
| |
| m_gs_id = 0; |
| } |
| |
| if (m_po_id != 0) |
| { |
| gl.deleteProgram(m_po_id); |
| |
| m_po_id = 0; |
| } |
| |
| if (m_tc_id != 0) |
| { |
| gl.deleteShader(m_tc_id); |
| |
| m_tc_id = 0; |
| } |
| |
| if (m_te_id != 0) |
| { |
| gl.deleteShader(m_te_id); |
| |
| m_te_id = 0; |
| } |
| } |
| |
| /** Executes all test cases stored in m_test_cases. |
| * |
| * This method throws a TestError exception upon test failure. |
| **/ |
| void DrawElementsBaseVertexTestBase::executeTestCases() |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| /* Iterate over all test cases */ |
| for (_test_cases_const_iterator test_cases_iterator = m_test_cases.begin(); |
| test_cases_iterator != m_test_cases.end(); ++test_cases_iterator) |
| { |
| const _test_case& test_case = *test_cases_iterator; |
| |
| /* What is the size of a single index value? */ |
| unsigned int index_size = 0; |
| |
| switch (test_case.index_type) |
| { |
| case GL_UNSIGNED_BYTE: |
| index_size = 1; |
| break; |
| case GL_UNSIGNED_SHORT: |
| index_size = 2; |
| break; |
| case GL_UNSIGNED_INT: |
| index_size = 4; |
| break; |
| |
| default: |
| { |
| TCU_FAIL("Unrecognized index type"); |
| } |
| } /* switch (test_case.index_type) */ |
| |
| /* Set up the work environment */ |
| setUpFunctionalTestObjects(test_case.use_clientside_vertex_data, test_case.use_clientside_index_data, |
| test_case.use_tessellation_shader_stage, test_case.use_geometry_shader_stage, |
| test_case.use_vertex_attrib_binding, test_case.use_overflow_test_vertices); |
| |
| gl.framebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_to_base_id, 0); /* level */ |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferTexture2D() call failed"); |
| |
| gl.clear(GL_COLOR_BUFFER_BIT); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glClear() call failed."); |
| |
| switch (test_case.function_type) |
| { |
| case FUNCTION_GL_DRAW_ELEMENTS_BASE_VERTEX: |
| { |
| gl.drawElementsBaseVertex(test_case.primitive_mode, 3, /* count */ |
| test_case.index_type, test_case.index_offset, test_case.basevertex); |
| |
| break; |
| } |
| |
| case FUNCTION_GL_DRAW_ELEMENTS_INSTANCED_BASE_VERTEX: |
| { |
| gl.drawElementsInstancedBaseVertex(test_case.primitive_mode, 3, /* count */ |
| test_case.index_type, test_case.index_offset, 3, /* instancecount */ |
| test_case.basevertex); |
| |
| break; |
| } |
| |
| case FUNCTION_GL_DRAW_RANGE_ELEMENTS_BASE_VERTEX: |
| { |
| gl.drawRangeElementsBaseVertex(test_case.primitive_mode, test_case.range_start, test_case.range_end, |
| 3, /* count */ |
| test_case.index_type, test_case.index_offset, test_case.basevertex); |
| |
| break; |
| } |
| |
| case FUNCTION_GL_MULTI_DRAW_ELEMENTS_BASE_VERTEX: |
| { |
| const glw::GLint basevertex_array[] = { test_case.basevertex, test_case.basevertex, test_case.basevertex }; |
| |
| gl.multiDrawElementsBaseVertex( |
| test_case.primitive_mode, test_case.multi_draw_call_count_array, test_case.index_type, |
| (const glw::GLvoid**)test_case.multi_draw_call_indices_array, 3, /* primcount */ |
| basevertex_array); |
| |
| break; |
| } |
| |
| default: |
| { |
| TCU_FAIL("Unsupported function index"); |
| } |
| } /* switch (n_function) */ |
| |
| if (gl.getError() != GL_NO_ERROR) |
| { |
| std::stringstream error_sstream; |
| |
| error_sstream << getFunctionName(test_case.function_type) << " call failed"; |
| TCU_FAIL(error_sstream.str().c_str()); |
| } |
| |
| /* Now, render the same triangle using glDrawElements() to the reference texture */ |
| gl.framebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_to_ref_id, 0); /* level */ |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferTexture2D() call failed"); |
| |
| gl.clear(GL_COLOR_BUFFER_BIT); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glClear() call failed."); |
| |
| glw::GLenum regular_draw_call_index_type = test_case.index_type; |
| if (test_case.regular_draw_call_index_type != 0) |
| { |
| /* Need to use a different index type for regular draw call */ |
| regular_draw_call_index_type = test_case.regular_draw_call_index_type; |
| |
| switch (test_case.regular_draw_call_index_type) |
| { |
| case GL_UNSIGNED_BYTE: |
| index_size = 1; |
| break; |
| case GL_UNSIGNED_SHORT: |
| index_size = 2; |
| break; |
| case GL_UNSIGNED_INT: |
| index_size = 4; |
| break; |
| |
| default: |
| { |
| TCU_FAIL("Unrecognized index type"); |
| } |
| } |
| } |
| |
| glw::GLubyte* regular_draw_call_offset = |
| (glw::GLubyte*)test_case.index_offset + index_size * test_case.regular_draw_call_offset; |
| if (test_case.use_overflow_test_vertices) |
| { |
| /* Special case for overflow test */ |
| regular_draw_call_offset = (glw::GLubyte*)test_case.regular_draw_call_offset2; |
| } |
| |
| switch (test_case.function_type) |
| { |
| case FUNCTION_GL_DRAW_ELEMENTS_BASE_VERTEX: /* pass-through */ |
| case FUNCTION_GL_DRAW_RANGE_ELEMENTS_BASE_VERTEX: |
| { |
| gl.drawElements(test_case.primitive_mode, 3, /* count */ |
| regular_draw_call_index_type, regular_draw_call_offset); /* as per test spec */ |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawElements() call failed"); |
| |
| break; |
| } |
| |
| case FUNCTION_GL_DRAW_ELEMENTS_INSTANCED_BASE_VERTEX: |
| { |
| gl.drawElementsInstanced(test_case.primitive_mode, 3, /* count */ |
| regular_draw_call_index_type, regular_draw_call_offset, 3); /* instancecount */ |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawElementsInstanced() call failed"); |
| |
| break; |
| } |
| |
| case FUNCTION_GL_MULTI_DRAW_ELEMENTS_BASE_VERTEX: |
| { |
| /* Normally we'd just use glMultiDrawElements() here but it's not a part |
| * of ES3.1 core spec and we're trying to avoid any dependencies in the test. |
| * No damage done under GL, either. |
| */ |
| for (unsigned int n_draw_call = 0; n_draw_call < 3; /* drawcount */ |
| ++n_draw_call) |
| { |
| gl.drawElements(test_case.primitive_mode, test_case.multi_draw_call_count_array[n_draw_call], |
| regular_draw_call_index_type, |
| (const glw::GLvoid*)test_case.regular_multi_draw_call_offseted_array[n_draw_call]); |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawElements() call failed."); |
| } |
| |
| break; |
| } |
| |
| default: |
| { |
| TCU_FAIL("Unrecognized function index"); |
| } |
| } /* switch (n_function) */ |
| |
| /* Compare the two textures and make sure they are either a match or not, |
| * depending on whether basevertex values match the offsets used for regular |
| * draw calls. |
| */ |
| compareBaseAndReferenceTextures(test_case.should_base_texture_match_reference_texture); |
| |
| /* ES Resources are allocated per test objects, we need to release them here */ |
| deinitPerTestObjects(); |
| } /* for (all test cases) */ |
| } |
| |
| /* Returns name of the function represented by _function_type. |
| * |
| * @param function_type Function type to use for the query. |
| * |
| * @return As per description, or "[?]" (without the quotation marks) |
| * if @param function_type was not recognized. |
| */ |
| std::string DrawElementsBaseVertexTestBase::getFunctionName(_function_type function_type) |
| { |
| std::string result = "[?]"; |
| |
| switch (function_type) |
| { |
| case FUNCTION_GL_DRAW_ELEMENTS_BASE_VERTEX: |
| { |
| if (glu::isContextTypeES(m_context.getRenderContext().getType())) |
| { |
| result = "glDrawElementsBaseVertexEXT()"; |
| } |
| else |
| { |
| result = "glDrawElementsBaseVertex()"; |
| } |
| |
| break; |
| } |
| |
| case FUNCTION_GL_DRAW_ELEMENTS_INSTANCED_BASE_VERTEX: |
| { |
| if (glu::isContextTypeES(m_context.getRenderContext().getType())) |
| { |
| result = "glDrawElementsInstancedBaseVertexEXT()"; |
| } |
| else |
| { |
| result = "glDrawElementsInstancedBaseVertex()"; |
| } |
| |
| break; |
| } |
| |
| case FUNCTION_GL_DRAW_RANGE_ELEMENTS_BASE_VERTEX: |
| { |
| if (glu::isContextTypeES(m_context.getRenderContext().getType())) |
| { |
| result = "glDrawRangeElementsBaseVertexEXT()"; |
| } |
| else |
| { |
| result = "glDrawRangeElementsBaseVertex()"; |
| } |
| |
| break; |
| } |
| |
| case FUNCTION_GL_MULTI_DRAW_ELEMENTS_BASE_VERTEX: |
| { |
| if (glu::isContextTypeES(m_context.getRenderContext().getType())) |
| { |
| result = "glMultiDrawElementsBaseVertexEXT()"; |
| } |
| else |
| { |
| result = "glMultiDrawElementsBaseVertex()"; |
| } |
| |
| break; |
| } |
| |
| default: |
| { |
| TCU_FAIL("Unknown function type"); |
| } |
| } /* switch (function_type) */ |
| |
| return result; |
| } |
| |
| /** Initializes extension-specific function pointers and caches information about |
| * extension availability. |
| * |
| * Function pointers for the following extensions are retrieved & stored: |
| * |
| * - GL_EXT_draw_elements_base_vertex (ES) or GL_ARB_draw_elements_base_vertex (GL) |
| * - GL_EXT_multi_draw_arrays (ES & GL) |
| * |
| * Availability of the following extensions is checked & cached: |
| * |
| * - GL_EXT_draw_elements_base_vertex (ES) or GL_ARB_draw_elements_base_vertex (GL) |
| * - GL_EXT_geometry_shader (ES) or GL_ARB_geometry_shader4 (GL) |
| * - GL_EXT_multi_draw_arrays (ES & GL) |
| * - GL_EXT_tessellation_shader (ES) or GL_ARB_tessellation_shader (GL) |
| */ |
| void DrawElementsBaseVertexTestBase::init() |
| { |
| TestCaseBase::init(); |
| |
| const glu::ContextInfo& context_info = m_context.getContextInfo(); |
| |
| if ((glu::isContextTypeES(m_context.getRenderContext().getType()) && |
| (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2)) || |
| context_info.isExtensionSupported("GL_EXT_draw_elements_base_vertex"))) || |
| context_info.isExtensionSupported("GL_ARB_draw_elements_base_vertex")) |
| { |
| #if defined(DE_DEBUG) && !defined(DE_COVERAGE_BUILD) |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| #endif |
| |
| m_is_draw_elements_base_vertex_supported = true; |
| |
| DE_ASSERT(gl.drawElementsBaseVertex != NULL); |
| DE_ASSERT(gl.drawElementsInstancedBaseVertex != NULL); |
| DE_ASSERT(gl.drawRangeElementsBaseVertex != NULL); |
| |
| /* NOTE: glMultiDrawElementsBaseVertex() is a part of the draw_elements_base_vertex extension in GL, |
| * but requires a separate extension under ES. |
| */ |
| if (!glu::isContextTypeES(m_context.getRenderContext().getType()) || |
| context_info.isExtensionSupported("GL_EXT_multi_draw_arrays")) |
| { |
| m_is_ext_multi_draw_arrays_supported = true; |
| |
| DE_ASSERT(gl.multiDrawElementsBaseVertex != NULL); |
| } /* if (GL_EXT_multi_draw_arrays is supported or GL context is being tested) */ |
| |
| if (glu::isContextTypeES(m_context.getRenderContext().getType())) |
| { |
| /* This conformance test needs to be adjusted in order to run under < ES3.1 contexts */ |
| DE_ASSERT(glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 1))); |
| } |
| else |
| { |
| m_is_vertex_attrib_binding_supported = context_info.isExtensionSupported("GL_ARB_vertex_attrib_binding"); |
| } |
| } /* if (GL_{ARB, EXT}_draw_elements_base_vertex is supported) */ |
| |
| if ((glu::isContextTypeES(m_context.getRenderContext().getType()) && |
| (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2)) || |
| context_info.isExtensionSupported("GL_EXT_geometry_shader"))) || |
| context_info.isExtensionSupported("GL_ARB_geometry_shader4")) |
| { |
| m_is_geometry_shader_supported = true; |
| } |
| |
| if ((glu::isContextTypeES(m_context.getRenderContext().getType()) && |
| (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2)) || |
| context_info.isExtensionSupported("GL_EXT_tessellation_shader"))) || |
| context_info.isExtensionSupported("GL_ARB_tessellation_shader")) |
| { |
| m_is_tessellation_shader_supported = true; |
| } |
| |
| if (!m_is_draw_elements_base_vertex_supported) |
| { |
| throw tcu::NotSupportedError("draw_elements_base_vertex functionality is not supported"); |
| } |
| } |
| |
| /** Sets up all ES objects required to run a single functional test case iteration. |
| * |
| * @param use_clientside_vertex_data true if the test case requires client-side buffers to |
| * back the color/vertex vertex attribute arrays; false |
| * to use buffer object storage. |
| * @param use_clientside_index_data true if the test case requires client-side buffers to |
| * be used as index data source; false to use buffer object |
| * storage. |
| * @param use_tessellation_shader_stage true if the program object used for the test should include |
| * tessellation control & evaluation shader stages; false to |
| * not to include these shader stages in the pipeline. |
| * @param use_geometry_shader_stage true if the program object used for the test should include |
| * geometry shader stage; false to not to include the shader |
| * stage in the pipeline. |
| * @param use_vertex_attrib_binding true if vertex attribute bindings should be used for |
| * vertex array object configuration. This also modifies the |
| * vertex shader, so that instead of calculating vertex color |
| * on a per-vertex basis, contents of the "color" input |
| * will be used as a data source for the color data. |
| * false to use a vertex attribute array configured with |
| * a casual glVertexAttribPointer() call. |
| * @param use_overflow_test_vertices true if using an especially large vertex array to test |
| * overflow behavior. |
| * |
| * This method can throw an exception if any of the ES calls fail. |
| **/ |
| void DrawElementsBaseVertexTestBase::setUpFunctionalTestObjects( |
| bool use_clientside_vertex_data, bool use_clientside_index_data, bool use_tessellation_shader_stage, |
| bool use_geometry_shader_stage, bool use_vertex_attrib_binding, bool use_overflow_test_vertices) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| /* Set up a texture object that we will use as a color attachment */ |
| gl.genTextures(1, &m_to_base_id); |
| gl.genTextures(1, &m_to_ref_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGenTextures() call(s) failed."); |
| |
| const glw::GLuint to_ids[] = { m_to_base_id, m_to_ref_id }; |
| const unsigned int n_to_ids = sizeof(to_ids) / sizeof(to_ids[0]); |
| |
| for (unsigned int n_to_id = 0; n_to_id < n_to_ids; ++n_to_id) |
| { |
| gl.bindTexture(GL_TEXTURE_2D, to_ids[n_to_id]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTexture() call failed."); |
| |
| gl.texStorage2D(GL_TEXTURE_2D, 1, /* levels */ |
| GL_RGBA8, m_to_width, m_to_height); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glTexStorage2D() call failed."); |
| } |
| |
| /* Also set up some buffers we will use for data comparison */ |
| m_to_base_data = new unsigned char[m_to_width * m_to_height * 4 /* components */]; |
| m_to_ref_data = new unsigned char[m_to_width * m_to_height * 4 /* components */]; |
| |
| /* Finally, for glReadPixels() operations, we need to make sure that pack alignment is set to 1 */ |
| gl.pixelStorei(GL_PACK_ALIGNMENT, 1); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glPixelStorei() call failed."); |
| |
| /* Proceed with framebuffer object initialization. Since we will be rendering to different |
| * render-targets, there's not much point in attaching any of the textures at this point. |
| */ |
| gl.genFramebuffers(1, &m_fbo_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGenFramebuffers() call failed."); |
| |
| gl.bindFramebuffer(GL_FRAMEBUFFER, m_fbo_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebufer() call failed"); |
| |
| gl.viewport(0, /* x */ |
| 0, /* y */ |
| m_to_width, m_to_height); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glViewport() call failed"); |
| |
| /* Set up buffer object we will use for the draw calls. Use the data |
| * from the test specification. |
| * |
| * NOTE: If you need to change the data layout here, make sure you also update |
| * m_draw_call_color_offset, m_draw_call_index*_offset, and |
| * m_draw_call_vertex*_offset setter calls elsewhere. |
| **/ |
| if (m_bo_id == 0) |
| { |
| unsigned int current_offset = 0; |
| |
| gl.genBuffers(1, &m_bo_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers() call failed."); |
| |
| gl.bindBuffer(GL_ARRAY_BUFFER, m_bo_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call failed."); |
| |
| gl.bufferData(GL_ARRAY_BUFFER, |
| m_bo_functional_data_index_size + m_bo_functional_data_vertex_size + |
| m_bo_functional2_data_vertex_size + m_bo_functional2_data_index_size + |
| m_bo_functional3_data_index_size + m_bo_functional4_data_index_size + |
| m_bo_functional5_data_index_size + m_bo_functional_data_color_size, |
| DE_NULL, /* data */ |
| GL_STATIC_DRAW); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() call failed."); |
| |
| gl.bufferSubData(GL_ARRAY_BUFFER, current_offset, m_bo_functional_data_vertex_size, |
| m_bo_functional_data_vertex); |
| current_offset += m_bo_functional_data_vertex_size; |
| |
| gl.bufferSubData(GL_ARRAY_BUFFER, current_offset, m_bo_functional2_data_vertex_size, |
| m_bo_functional2_data_vertex); |
| current_offset += m_bo_functional2_data_vertex_size; |
| |
| gl.bufferSubData(GL_ARRAY_BUFFER, current_offset, m_bo_functional_data_index_size, m_bo_functional_data_index); |
| current_offset += m_bo_functional_data_index_size; |
| |
| gl.bufferSubData(GL_ARRAY_BUFFER, current_offset, m_bo_functional2_data_index_size, |
| m_bo_functional2_data_index); |
| current_offset += m_bo_functional2_data_index_size; |
| |
| gl.bufferSubData(GL_ARRAY_BUFFER, current_offset, m_bo_functional3_data_index_size, |
| m_bo_functional3_data_index); |
| current_offset += m_bo_functional3_data_index_size; |
| |
| gl.bufferSubData(GL_ARRAY_BUFFER, current_offset, m_bo_functional4_data_index_size, |
| m_bo_functional4_data_index); |
| current_offset += m_bo_functional4_data_index_size; |
| |
| gl.bufferSubData(GL_ARRAY_BUFFER, current_offset, m_bo_functional5_data_index_size, |
| m_bo_functional5_data_index); |
| current_offset += m_bo_functional5_data_index_size; |
| |
| gl.bufferSubData(GL_ARRAY_BUFFER, current_offset, m_bo_functional_data_color_size, m_bo_functional_data_color); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferSubData() call(s) failed."); |
| } |
| |
| if (use_overflow_test_vertices && m_bo_id_2 == 0) |
| { |
| /* Create a special buffer that only has vertex data in a few slots */ |
| gl.genBuffers(1, &m_bo_id_2); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers() call failed"); |
| |
| gl.bindBuffer(GL_ARRAY_BUFFER, m_bo_id_2); |
| gl.bufferData(GL_ARRAY_BUFFER, 2 * 65550 * sizeof(glw::GLfloat), NULL, GL_STATIC_DRAW); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() call failed"); |
| |
| /* |
| * Upload to offset 0: regular draw call indices for making sure ubyte |
| * and ushort indices don't wrap around too early |
| * Upload to offset 256: base draw call indices of type ubyte |
| * Upload to offset 65536: base draw call indices of type ushort |
| */ |
| glw::GLfloat badVtx[] = { 0.9f, 0.9f, -0.9f, 0.9f, -0.9f, -0.9f, 0.9f, -0.9f, 0.9f, 0.9f, -0.9f, 0.9f, |
| -0.9f, -0.9f, 0.9f, -0.9f, 0.9f, 0.9f, -0.9f, 0.9f, -0.9f, -0.9f, 0.9f, -0.9f }; |
| gl.bufferSubData(GL_ARRAY_BUFFER, 0, sizeof(badVtx), badVtx); |
| gl.bufferSubData(GL_ARRAY_BUFFER, 2 * 256 * sizeof(glw::GLfloat), 2 * 12 * sizeof(glw::GLfloat), |
| m_bo_functional_data_vertex); |
| gl.bufferSubData(GL_ARRAY_BUFFER, 2 * 65536 * sizeof(glw::GLfloat), 2 * 12 * sizeof(glw::GLfloat), |
| m_bo_functional_data_vertex); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferSubData() call failed"); |
| } |
| |
| /* Set up the test program object */ |
| if (m_po_id == 0 || m_po_uses_gs_stage != use_geometry_shader_stage || |
| m_po_uses_tc_te_stages != use_tessellation_shader_stage || |
| m_po_uses_vertex_attrib_binding != use_vertex_attrib_binding) |
| |
| { |
| static const char* functional_fs_code = "${VERSION}\n" |
| "\n" |
| "precision highp float;\n" |
| "\n" |
| "in vec4 FS_COLOR_INPUT_NAME;\n" |
| "out vec4 result;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " result = FS_COLOR_INPUT_NAME;\n" |
| "}\n"; |
| static const char* functional_gs_code = "${VERSION}\n" |
| "${GEOMETRY_SHADER_REQUIRE}\n" |
| "\n" |
| "layout(triangles) in;\n" |
| "layout(triangle_strip, max_vertices = 3) out;\n" |
| "\n" |
| "in vec4 GS_COLOR_INPUT_NAME[];\n" |
| "out vec4 FS_COLOR_INPUT_NAME;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " for (int n = 0; n < 3; ++n)\n" |
| " {\n" |
| " gl_Position = vec4(gl_in[n].gl_Position.x, " |
| "-gl_in[n].gl_Position.y, gl_in[n].gl_Position.zw);\n" |
| " FS_COLOR_INPUT_NAME = GS_COLOR_INPUT_NAME[n];\n" |
| "\n" |
| " EmitVertex();\n" |
| " }\n" |
| "\n" |
| " EndPrimitive();\n" |
| "}\n"; |
| static const char* functional_tc_code = |
| "${VERSION}\n" |
| "${TESSELLATION_SHADER_REQUIRE}\n" |
| "\n" |
| "in vec4 TC_COLOR_INPUT_NAME[];\n" |
| "out vec4 TE_COLOR_INPUT_NAME[];\n" |
| "\n" |
| "layout(vertices = 3) out;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " gl_out [gl_InvocationID].gl_Position = gl_in [gl_InvocationID].gl_Position;\n" |
| " TE_COLOR_INPUT_NAME[gl_InvocationID] = TC_COLOR_INPUT_NAME[gl_InvocationID];\n" |
| "\n" |
| " gl_TessLevelInner[0] = 3.0;\n" |
| " gl_TessLevelOuter[0] = 3.0;\n" |
| " gl_TessLevelOuter[1] = 3.0;\n" |
| " gl_TessLevelOuter[2] = 3.0;\n" |
| "}\n"; |
| static const char* functional_te_code = |
| "${VERSION}\n" |
| "\n" |
| "${TESSELLATION_SHADER_REQUIRE}\n" |
| "\n" |
| "layout(triangles, equal_spacing, cw) in;\n" |
| "\n" |
| "in vec4 TE_COLOR_INPUT_NAME[];\n" |
| "out vec4 TE_COLOR_OUTPUT_NAME;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " vec2 p1 = gl_in[0].gl_Position.xy;\n" |
| " vec2 p2 = gl_in[1].gl_Position.xy;\n" |
| " vec2 p3 = gl_in[2].gl_Position.xy;\n" |
| "\n" |
| " TE_COLOR_OUTPUT_NAME = TE_COLOR_INPUT_NAME[0] + TE_COLOR_INPUT_NAME[1] + TE_COLOR_INPUT_NAME[2];\n" |
| " gl_Position = vec4(gl_TessCoord.x * p1.x + gl_TessCoord.y * p2.x + gl_TessCoord.z * p3.x,\n" |
| " gl_TessCoord.x * p1.y + gl_TessCoord.y * p2.y + gl_TessCoord.z * p3.y,\n" |
| " 0.0,\n" |
| " 1.0);\n" |
| "}\n"; |
| static const char* functional_vs_code = "${VERSION}\n" |
| "\n" |
| "out vec4 VS_COLOR_OUTPUT_NAME;\n" |
| "in vec4 vertex;\n" |
| "\n" |
| "OPTIONAL_USE_VERTEX_ATTRIB_BINDING_DEFINITIONS\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " float vertex_id_float = float(gl_VertexID);\n" |
| "\n" |
| "#ifdef USE_VERTEX_ATTRIB_BINDING\n" |
| " vec4 color = in_color;\n" |
| "#else\n" |
| " vec4 color = vec4(vertex_id_float / 7.0,\n" |
| " vertex_id_float / 3.0,\n" |
| " vertex_id_float / 17.0,\n" |
| " 1.0);\n" |
| "#endif\n" |
| " float scale = (gl_InstanceID == 0) ? 1.0 :\n" |
| " (gl_InstanceID == 1) ? 0.8 :\n" |
| " 0.5;\n" |
| "\n" |
| " VS_COLOR_OUTPUT_NAME = color;\n" |
| " gl_Position = vec4(vertex.xy * scale, vertex.zw);\n" |
| "}\n"; |
| |
| /* Release a program object if one has already been set up for the running test */ |
| deinitProgramAndShaderObjects(); |
| |
| /* Replace the tokens with actual variable names */ |
| std::string fs_body = specializeShader(1, &functional_fs_code); |
| std::string fs_color_input_name_token = "FS_COLOR_INPUT_NAME"; |
| std::string fs_color_input_name_token_value; |
| std::string gs_body = specializeShader(1, &functional_gs_code); |
| std::string gs_color_input_name_token = "GS_COLOR_INPUT_NAME"; |
| std::string gs_color_input_name_token_value; |
| std::string tc_body = specializeShader(1, &functional_tc_code); |
| std::string tc_color_input_name_token = "TC_COLOR_INPUT_NAME"; |
| std::string tc_color_input_name_token_value; |
| std::string te_body = specializeShader(1, &functional_te_code); |
| std::string te_color_input_name_token = "TE_COLOR_INPUT_NAME"; |
| std::string te_color_input_name_token_value; |
| std::string te_color_output_name_token = "TE_COLOR_OUTPUT_NAME"; |
| std::string te_color_output_name_token_value; |
| std::string vs_body = specializeShader(1, &functional_vs_code); |
| std::string vs_color_output_name_token = "VS_COLOR_OUTPUT_NAME"; |
| std::string vs_color_output_name_token_value; |
| std::string vs_optional_use_vertex_attrib_binding_definitions_token = |
| "OPTIONAL_USE_VERTEX_ATTRIB_BINDING_DEFINITIONS"; |
| std::string vs_optional_use_vertex_attrib_binding_definitions_token_value; |
| |
| std::string* bodies[] = { &fs_body, &gs_body, &tc_body, &te_body, &vs_body }; |
| const unsigned int n_bodies = sizeof(bodies) / sizeof(bodies[0]); |
| |
| std::string* token_value_pairs[] = { &fs_color_input_name_token, |
| &fs_color_input_name_token_value, |
| &gs_color_input_name_token, |
| &gs_color_input_name_token_value, |
| &tc_color_input_name_token, |
| &tc_color_input_name_token_value, |
| &te_color_input_name_token, |
| &te_color_input_name_token_value, |
| &te_color_output_name_token, |
| &te_color_output_name_token_value, |
| &vs_color_output_name_token, |
| &vs_color_output_name_token_value, |
| &vs_optional_use_vertex_attrib_binding_definitions_token, |
| &vs_optional_use_vertex_attrib_binding_definitions_token_value }; |
| const unsigned int n_token_value_pairs = |
| sizeof(token_value_pairs) / sizeof(token_value_pairs[0]) / 2 /* token+value */; |
| |
| if (!use_tessellation_shader_stage) |
| { |
| if (!use_geometry_shader_stage) |
| { |
| /* Geometry & tessellation shader stages are not required. |
| * |
| * NOTE: This code-path is also used by Functional Test VII which verifies that |
| * vertex attribute bindings work correctly with basevertex draw calls. |
| * The test only uses FS & VS in its rendering pipeline and consumes a color |
| * taken from an enabled vertex attribute array instead of a vector value |
| * calculated in vertex shader stage, |
| */ |
| vs_color_output_name_token_value = "out_vs_color"; |
| fs_color_input_name_token_value = vs_color_output_name_token_value; |
| |
| if (use_vertex_attrib_binding) |
| { |
| vs_optional_use_vertex_attrib_binding_definitions_token_value = |
| "#define USE_VERTEX_ATTRIB_BINDING\n" |
| "in vec4 in_color;\n"; |
| } |
| } /* if (!use_geometry_shader_stage) */ |
| else |
| { |
| /* Geometry shader stage is needed, but tessellation shader stage |
| * can be skipped */ |
| fs_color_input_name_token_value = "out_fs_color"; |
| gs_color_input_name_token_value = "out_vs_color"; |
| vs_color_output_name_token_value = "out_vs_color"; |
| |
| DE_ASSERT(!use_vertex_attrib_binding); |
| } |
| } /* if (!use_tessellation_shader_stage) */ |
| else |
| { |
| DE_ASSERT(!use_vertex_attrib_binding); |
| |
| if (!use_geometry_shader_stage) |
| { |
| /* Tessellation shader stage is needed, but geometry shader stage |
| * can be skipped */ |
| fs_color_input_name_token_value = "out_fs_color"; |
| tc_color_input_name_token_value = "out_vs_color"; |
| te_color_input_name_token_value = "out_tc_color"; |
| te_color_output_name_token_value = "out_fs_color"; |
| vs_color_output_name_token_value = "out_vs_color"; |
| } /* if (!use_geometry_shader_stage) */ |
| else |
| { |
| /* Both tessellation and geometry shader stages are needed */ |
| fs_color_input_name_token_value = "out_fs_color"; |
| gs_color_input_name_token_value = "out_te_color"; |
| tc_color_input_name_token_value = "out_vs_color"; |
| te_color_input_name_token_value = "out_tc_color"; |
| te_color_output_name_token_value = "out_te_color"; |
| vs_color_output_name_token_value = "out_vs_color"; |
| } |
| } |
| |
| for (unsigned int n_body = 0; n_body < n_bodies; ++n_body) |
| { |
| std::string* body_ptr = bodies[n_body]; |
| |
| for (unsigned int n_token_value_pair = 0; n_token_value_pair < n_token_value_pairs; ++n_token_value_pair) |
| { |
| std::string token = *token_value_pairs[2 * n_token_value_pair + 0]; |
| std::size_t token_location = std::string::npos; |
| std::string value = *token_value_pairs[2 * n_token_value_pair + 1]; |
| |
| while ((token_location = body_ptr->find(token)) != std::string::npos) |
| { |
| body_ptr->replace(token_location, token.length(), value); |
| } /* while (tokens are found) */ |
| } /* for (all token+value pairs) */ |
| } /* for (all bodies) */ |
| |
| /* Build the actual program */ |
| buildProgram(fs_body.c_str(), vs_body.c_str(), use_tessellation_shader_stage ? tc_body.c_str() : DE_NULL, |
| use_tessellation_shader_stage ? te_body.c_str() : DE_NULL, |
| use_geometry_shader_stage ? gs_body.c_str() : DE_NULL); |
| |
| m_po_uses_gs_stage = use_geometry_shader_stage; |
| m_po_uses_tc_te_stages = use_tessellation_shader_stage; |
| m_po_uses_vertex_attrib_binding = use_vertex_attrib_binding; |
| } |
| |
| /* Set up the vertex array object */ |
| if (m_vao_id != 0) |
| { |
| gl.deleteVertexArrays(1, &m_vao_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteVertexArrays() call failed."); |
| |
| m_vao_id = 0; |
| } |
| |
| if (!use_clientside_vertex_data) |
| { |
| gl.genVertexArrays(1, &m_vao_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGenVertexArrays() call failed."); |
| } |
| gl.bindVertexArray(m_vao_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray() call failed."); |
| |
| if (m_po_color_attribute_location != -1) |
| { |
| DE_ASSERT(use_vertex_attrib_binding); |
| |
| gl.enableVertexAttribArray(m_po_color_attribute_location); |
| } |
| |
| gl.enableVertexAttribArray(m_po_vertex_attribute_location); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray() call(s) failed."); |
| |
| /* Configure the VAO */ |
| if (use_clientside_index_data) |
| { |
| gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call failed."); |
| } |
| else |
| { |
| gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_bo_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call failed."); |
| } |
| |
| if (use_clientside_vertex_data) |
| { |
| gl.bindBuffer(GL_ARRAY_BUFFER, 0); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call failed."); |
| } |
| else |
| { |
| gl.bindBuffer(GL_ARRAY_BUFFER, use_overflow_test_vertices ? m_bo_id_2 : m_bo_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call failed."); |
| } |
| |
| if (!use_vertex_attrib_binding) |
| { |
| DE_ASSERT(m_po_color_attribute_location == -1); |
| |
| gl.vertexAttribPointer(m_po_vertex_attribute_location, 2, /* size */ |
| GL_FLOAT, GL_FALSE, /* normalized */ |
| 0, /* stride */ |
| m_draw_call_vertex_offset); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribPointer() call failed."); |
| } /* if (!use_vertex_attrib_binding) */ |
| else |
| { |
| DE_ASSERT(m_po_color_attribute_location != -1); |
| DE_ASSERT(m_draw_call_vertex_offset < m_draw_call_color_offset); |
| |
| gl.vertexAttribFormat(m_po_color_attribute_location, 2, /* size */ |
| GL_FLOAT, /* type */ |
| GL_FALSE, /* normalized */ |
| (glw::GLuint)((const glw::GLubyte*)m_draw_call_color_offset - |
| (const glw::GLubyte*)m_draw_call_vertex_offset)); |
| gl.vertexAttribFormat(m_po_vertex_attribute_location, 2, /* size */ |
| GL_FLOAT, /* type */ |
| GL_FALSE, /* normalized */ |
| 0); /* relativeoffset */ |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribFormat() call(s) failed."); |
| |
| gl.vertexAttribBinding(m_po_color_attribute_location, 0); /* bindingindex */ |
| gl.vertexAttribBinding(m_po_vertex_attribute_location, 0); /* bindingindex */ |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribBinding() call(s) failed."); |
| |
| gl.bindVertexBuffer(0, /* bindingindex */ |
| (use_clientside_vertex_data) ? 0 : (use_overflow_test_vertices ? m_bo_id_2 : m_bo_id), |
| (glw::GLintptr)m_draw_call_vertex_offset, sizeof(float) * 2); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexBuffer() call failed."); |
| } |
| |
| /* Bind the program object to the rendering context */ |
| gl.useProgram(m_po_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() call failed."); |
| } |
| |
| /** Sets up all ES objects required to run a single negative test case iteration. |
| * |
| * @param use_clientside_vertex_data true if the test case requires client-side buffers to |
| * back the color/vertex vertex attribute arrays; false |
| * to use buffer object storage. |
| * @param use_clientside_index_data true if the test case requires client-side buffers to |
| * be used as index data source; false to use buffer object |
| * storage. |
| */ |
| void DrawElementsBaseVertexTestBase::setUpNegativeTestObjects(bool use_clientside_vertex_data, |
| bool use_clientside_index_data) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| /* Set up buffer object we will use for the draw calls. Use the data |
| * from the test specification */ |
| if (m_bo_id == 0) |
| { |
| gl.genBuffers(1, &m_bo_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers() call failed."); |
| |
| gl.bindBuffer(GL_ARRAY_BUFFER, m_bo_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call failed."); |
| |
| gl.bufferData(GL_ARRAY_BUFFER, m_bo_negative_data_index_size + m_bo_negative_data_vertex_size, |
| DE_NULL, /* data */ |
| GL_STATIC_DRAW); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() call failed."); |
| |
| gl.bufferSubData(GL_ARRAY_BUFFER, 0, /* offset */ |
| m_bo_negative_data_vertex_size, m_bo_negative_data_vertex); |
| gl.bufferSubData(GL_ARRAY_BUFFER, m_bo_negative_data_vertex_size, /* offset */ |
| m_bo_negative_data_index_size, m_bo_negative_data_index); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferSubData() call(s) failed."); |
| } |
| |
| if (use_clientside_index_data) |
| { |
| m_draw_call_index_offset = m_bo_negative_data_index; |
| } |
| else |
| { |
| m_draw_call_index_offset = (const glw::GLuint*)(deUintptr)m_bo_negative_data_vertex_size; |
| } |
| |
| if (use_clientside_vertex_data) |
| { |
| m_draw_call_vertex_offset = m_bo_negative_data_vertex; |
| } |
| else |
| { |
| m_draw_call_vertex_offset = DE_NULL; |
| } |
| |
| /* Set up the test program object */ |
| if (m_po_id == 0) |
| { |
| static const char* negative_fs_code = "${VERSION}\n" |
| "precision highp float;\n" |
| "\n" |
| "out vec4 result;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " result = vec4(1.0);\n" |
| "}\n"; |
| static const char* negative_vs_code = "${VERSION}\n" |
| "\n" |
| "in vec4 vertex;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = vertex;\n" |
| "}\n"; |
| |
| std::string fs_specialized_code = specializeShader(1, &negative_fs_code); |
| const char* fs_specialized_code_raw = fs_specialized_code.c_str(); |
| std::string vs_specialized_code = specializeShader(1, &negative_vs_code); |
| const char* vs_specialized_code_raw = vs_specialized_code.c_str(); |
| |
| buildProgram(fs_specialized_code_raw, vs_specialized_code_raw, DE_NULL, /* tc_code */ |
| DE_NULL, /* te_code */ |
| DE_NULL); /* gs_code */ |
| } |
| |
| /* Set up a vertex array object */ |
| if (m_vao_id == 0) |
| { |
| gl.genVertexArrays(1, &m_vao_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGenVertexArrays() call failed."); |
| |
| gl.bindVertexArray(m_vao_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray() call failed."); |
| |
| gl.enableVertexAttribArray(m_po_vertex_attribute_location); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray() call failed."); |
| } |
| |
| /* Configure the VAO */ |
| if (use_clientside_index_data) |
| { |
| gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call failed."); |
| } |
| else |
| { |
| gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_bo_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call failed."); |
| } |
| |
| if (use_clientside_vertex_data) |
| { |
| gl.bindBuffer(GL_ARRAY_BUFFER, 0); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call failed."); |
| |
| gl.bindVertexArray(0); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray() call failed."); |
| } |
| else |
| { |
| gl.bindBuffer(GL_ARRAY_BUFFER, m_bo_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call failed."); |
| } |
| |
| gl.vertexAttribPointer(m_po_vertex_attribute_location, 2, /* size */ |
| GL_FLOAT, GL_FALSE, /* normalized */ |
| 0, /* stride */ |
| m_draw_call_vertex_offset); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribPointer() call failed."); |
| |
| /* Bind the program object to the rendering context */ |
| gl.useProgram(m_po_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() call failed."); |
| } |
| |
| /** Constructor. |
| * |
| * @param context Rendering context handle. |
| **/ |
| DrawElementsBaseVertexFunctionalCorrectBaseVertexBehavior::DrawElementsBaseVertexFunctionalCorrectBaseVertexBehavior( |
| Context& context, const ExtParameters& extParams) |
| : DrawElementsBaseVertexTestBase(context, extParams, "basevertex_behavior1", |
| "Verifies basevertex draw calls work correctly for a number of " |
| "different rendering pipeline and VAO configurations") |
| { |
| /* Left blank on purpose */ |
| } |
| |
| /** Sets up test case descriptors for the test instance. These will later be used |
| * as input for DrawElementsBaseVertexTestBase::executeTestCases(), which performs |
| * the actual testing. |
| **/ |
| void DrawElementsBaseVertexFunctionalCorrectBaseVertexBehavior::setUpTestCases() |
| { |
| /* Set up test case descriptors */ |
| const glw::GLint basevertex_values[] = { 10, 0 }; |
| const unsigned int n_basevertex_values = sizeof(basevertex_values) / sizeof(basevertex_values[0]); |
| |
| /* The test needs to be run in two iterations, using client-side memory and buffer object |
| * for index data respectively |
| */ |
| for (int vao_iteration = 0; vao_iteration < 2; ++vao_iteration) |
| { |
| /* Skip client-side vertex array as gl_VertexID is undefined when no buffer bound to ARRAY_BUFFER |
| * See section 11.1.3.9 in OpenGL ES 3.1 spec |
| */ |
| bool use_clientside_vertex_data = 0; |
| bool use_clientside_index_data = ((vao_iteration & (1 << 0)) != 0); |
| |
| /* OpenGL does not support client-side data. */ |
| if (!glu::isContextTypeES(m_context.getRenderContext().getType())) |
| { |
| if (use_clientside_index_data || use_clientside_vertex_data) |
| { |
| continue; |
| } |
| } |
| |
| /* Compute the offsets */ |
| computeVBODataOffsets(use_clientside_index_data, use_clientside_vertex_data); |
| |
| /* There are two index data sets we need to iterate over */ |
| const glw::GLuint* index_offsets[] = { m_draw_call_index_offset, m_draw_call_index2_offset }; |
| const unsigned int n_index_offsets = sizeof(index_offsets) / sizeof(index_offsets[0]); |
| |
| for (unsigned int n_index_offset = 0; n_index_offset < n_index_offsets; ++n_index_offset) |
| { |
| const glw::GLuint* current_index_offset = index_offsets[n_index_offset]; |
| |
| /* We need to test four different functions: |
| * |
| * a) glDrawElementsBaseVertex() (GL) |
| * or glDrawElementsBaseVertexEXT() (ES) |
| * b) glDrawRangeElementsBaseVertex() (GL) |
| * or glDrawRangeElementsBaseVertexEXT() (ES) |
| * c) glDrawElementsInstancedBaseVertex() (GL) |
| * or glDrawElementsInstancedBaseVertexEXT() (ES) |
| * d) glMultiDrawElementsBaseVertex() (GL) |
| * or glMultiDrawElementsBaseVertexEXT() (ES) (if supported) |
| **/ |
| for (int n_function = 0; n_function < FUNCTION_COUNT; ++n_function) |
| { |
| /* Do not try to use the multi draw call if relevant extension is |
| * not supported. */ |
| if (!m_is_ext_multi_draw_arrays_supported && n_function == FUNCTION_GL_MULTI_DRAW_ELEMENTS_BASE_VERTEX) |
| { |
| continue; |
| } |
| |
| /* We need to run the test for a number of different basevertex values. */ |
| for (unsigned int n_basevertex_value = 0; n_basevertex_value < n_basevertex_values; |
| ++n_basevertex_value) |
| { |
| /* Finally, we want to verify that basevertex draw calls work correctly both when vertex attribute |
| * bindings are enabled and disabled */ |
| bool vertex_attrib_binding_statuses[] = { false, true }; |
| unsigned int n_vertex_attrib_binding_statuses = |
| sizeof(vertex_attrib_binding_statuses) / sizeof(vertex_attrib_binding_statuses[0]); |
| |
| for (unsigned int n_vertex_attrib_binding_status = 0; |
| n_vertex_attrib_binding_status < n_vertex_attrib_binding_statuses; |
| ++n_vertex_attrib_binding_status) |
| { |
| bool use_vertex_attrib_binding = vertex_attrib_binding_statuses[n_vertex_attrib_binding_status]; |
| |
| /* Under GL, "vertex attrib binding" functionality is only available if GL_ARB_vertex_attrib_binding |
| * extension is supported. |
| */ |
| if (!m_is_vertex_attrib_binding_supported && use_vertex_attrib_binding) |
| { |
| continue; |
| } |
| |
| /* Prepare a few arrays so that we can handle the multi draw call and its emulated version.. */ |
| const glw::GLsizei multi_draw_call_count_array[3] = { 3, 6, 3 }; |
| const glw::GLuint* multi_draw_call_indices_array[3] = { |
| (glw::GLuint*)(current_index_offset), (glw::GLuint*)(current_index_offset + 3), |
| (glw::GLuint*)(current_index_offset + 9) |
| }; |
| |
| /* Reference texture should always reflect basevertex=10 behavior. */ |
| const glw::GLuint regular_draw_call_offset = basevertex_values[0]; |
| const glw::GLuint* regular_multi_draw_call_offseted_array[3] = { |
| multi_draw_call_indices_array[0] + regular_draw_call_offset, |
| multi_draw_call_indices_array[1] + regular_draw_call_offset, |
| multi_draw_call_indices_array[2] + regular_draw_call_offset, |
| }; |
| |
| /* Construct the test case descriptor */ |
| _test_case new_test_case; |
| |
| new_test_case.basevertex = basevertex_values[n_basevertex_value]; |
| new_test_case.function_type = (_function_type)n_function; |
| new_test_case.index_offset = current_index_offset; |
| new_test_case.range_start = n_index_offset == 0 ? 0 : 10; |
| new_test_case.range_end = n_index_offset == 0 ? 22 : 32; |
| new_test_case.index_type = GL_UNSIGNED_INT; |
| new_test_case.primitive_mode = GL_TRIANGLES; |
| new_test_case.regular_draw_call_offset = basevertex_values[0]; |
| new_test_case.should_base_texture_match_reference_texture = |
| ((glw::GLuint)new_test_case.basevertex == new_test_case.regular_draw_call_offset); |
| new_test_case.use_clientside_index_data = use_clientside_index_data; |
| new_test_case.use_clientside_vertex_data = use_clientside_vertex_data; |
| new_test_case.use_geometry_shader_stage = false; |
| new_test_case.use_tessellation_shader_stage = false; |
| new_test_case.use_vertex_attrib_binding = use_vertex_attrib_binding; |
| |
| memcpy(new_test_case.multi_draw_call_count_array, multi_draw_call_count_array, |
| sizeof(multi_draw_call_count_array)); |
| memcpy(new_test_case.multi_draw_call_indices_array, multi_draw_call_indices_array, |
| sizeof(multi_draw_call_indices_array)); |
| memcpy(new_test_case.regular_multi_draw_call_offseted_array, |
| regular_multi_draw_call_offseted_array, sizeof(regular_multi_draw_call_offseted_array)); |
| |
| m_test_cases.push_back(new_test_case); |
| } /* for (all vertex_attrib_binding statuses) */ |
| } /* for (all basevertex values) */ |
| } /* for (all four functions) */ |
| } /* for (all index data sets) */ |
| } /* for (all VAO iterations) */ |
| } |
| |
| /** Executes test iteration. |
| * |
| * @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed. |
| */ |
| tcu::TestNode::IterateResult DrawElementsBaseVertexFunctionalCorrectBaseVertexBehavior::iterate() |
| { |
| setUpTestCases(); |
| executeTestCases(); |
| |
| /* Test case passed */ |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| |
| return STOP; |
| } |
| |
| /** Constructor. |
| * |
| * @param context Rendering context handle. |
| **/ |
| DrawElementsBaseVertexFunctionalCorrectBaseVertexBehavior2::DrawElementsBaseVertexFunctionalCorrectBaseVertexBehavior2( |
| Context& context, const ExtParameters& extParams) |
| : DrawElementsBaseVertexTestBase(context, extParams, "basevertex_behavior2", |
| "Verifies basevertex draw calls work correctly for a number of " |
| "different rendering pipeline and VAO configurations. Uses slightly " |
| "different data set than basevertex_behavior.") |
| { |
| /* Left blank on purpose */ |
| } |
| |
| /** Sets up test case descriptors for the test instance. These will later be used |
| * as input for DrawElementsBaseVertexTestBase::executeTestCases(), which performs |
| * the actual testing. |
| **/ |
| void DrawElementsBaseVertexFunctionalCorrectBaseVertexBehavior2::setUpTestCases() |
| { |
| /* Set up test case descriptors */ |
| const glw::GLint basevertex = 5; |
| |
| /* The test needs to be run in two iterations, using client-side memory and buffer object |
| * for index data respectively |
| */ |
| for (int vao_iteration = 0; vao_iteration < 2; ++vao_iteration) |
| { |
| /* Skip client-side vertex array as gl_VertexID is undefined when no buffer bound to ARRAY_BUFFER |
| * See section 11.1.3.9 in OpenGL ES 3.1 spec |
| */ |
| bool use_clientside_vertex_data = 0; |
| bool use_clientside_index_data = ((vao_iteration & (1 << 0)) != 0); |
| |
| /* OpenGL does not support client-side data. */ |
| if (!glu::isContextTypeES(m_context.getRenderContext().getType())) |
| { |
| if (use_clientside_index_data || use_clientside_vertex_data) |
| { |
| continue; |
| } |
| } |
| |
| /* Compute the offsets */ |
| computeVBODataOffsets(use_clientside_index_data, use_clientside_vertex_data); |
| |
| /* We need to test four different functions: |
| * |
| * a) glDrawElementsBaseVertex() (GL) |
| * or glDrawElementsBaseVertexEXT() (ES) |
| * b) glDrawRangeElementsBaseVertex() (GL) |
| * or glDrawRangeElementsBaseVertexEXT() (ES) |
| * c) glDrawElementsInstancedBaseVertex() (GL) |
| * or glDrawElementsInstancedBaseVertexEXT() (ES) |
| * d) glMultiDrawElementsBaseVertex() (GL) |
| * or glMultiDrawElementsBaseVertexEXT() (ES) (if supported) |
| **/ |
| for (int n_function = 0; n_function < FUNCTION_COUNT; ++n_function) |
| { |
| /* Do not try to use the multi draw call if relevant extension is |
| * not supported. */ |
| if (!m_is_ext_multi_draw_arrays_supported && n_function == FUNCTION_GL_MULTI_DRAW_ELEMENTS_BASE_VERTEX) |
| { |
| continue; |
| } |
| |
| /* Prepare a few arrays so that we can handle the multi draw call and its emulated version.. */ |
| const glw::GLsizei multi_draw_call_count_array[3] = { 3, 6, 3 }; |
| const glw::GLuint* multi_draw_call_indices_array[3] = { (glw::GLuint*)(m_draw_call_index_offset), |
| (glw::GLuint*)(m_draw_call_index_offset + 3), |
| (glw::GLuint*)(m_draw_call_index_offset + 9) }; |
| |
| /* Reference texture should always reflect basevertex=10 behavior. */ |
| const glw::GLuint regular_draw_call_offset = 0; |
| const glw::GLuint* regular_multi_draw_call_offseted_array[3] = { |
| multi_draw_call_indices_array[0] + regular_draw_call_offset, |
| multi_draw_call_indices_array[1] + regular_draw_call_offset, |
| multi_draw_call_indices_array[2] + regular_draw_call_offset, |
| }; |
| |
| /* Construct the test case descriptor */ |
| _test_case new_test_case; |
| |
| new_test_case.basevertex = basevertex; |
| new_test_case.function_type = (_function_type)n_function; |
| new_test_case.index_offset = m_draw_call_index_offset; |
| new_test_case.range_start = 0; |
| new_test_case.range_end = 22; |
| new_test_case.index_type = GL_UNSIGNED_INT; |
| new_test_case.primitive_mode = GL_TRIANGLES; |
| new_test_case.regular_draw_call_offset = regular_draw_call_offset; |
| new_test_case.should_base_texture_match_reference_texture = |
| ((glw::GLuint)new_test_case.basevertex == new_test_case.regular_draw_call_offset); |
| new_test_case.use_clientside_index_data = use_clientside_index_data; |
| new_test_case.use_clientside_vertex_data = use_clientside_vertex_data; |
| new_test_case.use_geometry_shader_stage = false; |
| new_test_case.use_tessellation_shader_stage = false; |
| new_test_case.use_vertex_attrib_binding = false; |
| |
| memcpy(new_test_case.multi_draw_call_count_array, multi_draw_call_count_array, |
| sizeof(multi_draw_call_count_array)); |
| memcpy(new_test_case.multi_draw_call_indices_array, multi_draw_call_indices_array, |
| sizeof(multi_draw_call_indices_array)); |
| memcpy(new_test_case.regular_multi_draw_call_offseted_array, regular_multi_draw_call_offseted_array, |
| sizeof(regular_multi_draw_call_offseted_array)); |
| |
| m_test_cases.push_back(new_test_case); |
| } /* for (all four functions) */ |
| } /* for (all VAO iterations) */ |
| } |
| |
| /** Executes test iteration. |
| * |
| * @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed. |
| */ |
| tcu::TestNode::IterateResult DrawElementsBaseVertexFunctionalCorrectBaseVertexBehavior2::iterate() |
| { |
| setUpTestCases(); |
| executeTestCases(); |
| |
| /* Test case passed */ |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| |
| return STOP; |
| } |
| |
| /** Constructor. |
| * |
| * @param context Rendering context handle. |
| **/ |
| DrawElementsBaseVertexFunctionalCorrectBaseVertexBehaviorUnderflow:: |
| DrawElementsBaseVertexFunctionalCorrectBaseVertexBehaviorUnderflow(Context& context, const ExtParameters& extParams) |
| : DrawElementsBaseVertexTestBase(context, extParams, "underflow", |
| "Verifies basevertex draw calls work correctly for negative " |
| "basevertex values") |
| { |
| /* Left blank on purpose */ |
| } |
| |
| /** Sets up test case descriptors for the test instance. These will later be used |
| * as input for DrawElementsBaseVertexTestBase::executeTestCases(), which performs |
| * the actual testing. |
| **/ |
| void DrawElementsBaseVertexFunctionalCorrectBaseVertexBehaviorUnderflow::setUpTestCases() |
| { |
| /* Set up test case descriptors */ |
| const glw::GLint basevertex = -10; |
| |
| /* The test needs to be run in two iterations, using client-side memory and buffer object |
| * for index data respectively |
| */ |
| for (int vao_iteration = 0; vao_iteration < 2; ++vao_iteration) |
| { |
| /* Skip client-side vertex array as gl_VertexID is undefined when no buffer bound to ARRAY_BUFFER |
| * See section 11.1.3.9 in OpenGL ES 3.1 spec |
| */ |
| bool use_clientside_vertex_data = 0; |
| bool use_clientside_index_data = ((vao_iteration & (1 << 0)) != 0); |
| |
| /* OpenGL does not support client-side data. */ |
| if (!glu::isContextTypeES(m_context.getRenderContext().getType())) |
| { |
| if (use_clientside_index_data || use_clientside_vertex_data) |
| { |
| continue; |
| } |
| } |
| |
| /* Compute the offsets */ |
| computeVBODataOffsets(use_clientside_index_data, use_clientside_vertex_data); |
| |
| /* We need to test four different functions: |
| * |
| * a) glDrawElementsBaseVertex() (GL) |
| * or glDrawElementsBaseVertexEXT() (ES) |
| * b) glDrawRangeElementsBaseVertex() (GL) |
| * or glDrawRangeElementsBaseVertexEXT() (ES) |
| * c) glDrawElementsInstancedBaseVertex() (GL) |
| * or glDrawElementsInstancedBaseVertexEXT() (ES) |
| * d) glMultiDrawElementsBaseVertex() (GL) |
| * or glMultiDrawElementsBaseVertexEXT() (ES) (if supported) |
| **/ |
| for (int n_function = 0; n_function < FUNCTION_COUNT; ++n_function) |
| { |
| /* Do not try to use the multi draw call if relevant extension is |
| * not supported. */ |
| if (!m_is_ext_multi_draw_arrays_supported && n_function == FUNCTION_GL_MULTI_DRAW_ELEMENTS_BASE_VERTEX) |
| { |
| continue; |
| } |
| |
| /* Prepare a few arrays so that we can handle the multi draw call and its emulated version.. */ |
| unsigned int offset_from_10_to_0_index = 23; /* this offset moves us from the start index of 10 to a zero |
| * index, given the second index data set we will be using. |
| * Please see declaration of functional2_index_data if you |
| * need to verify this by yourself. */ |
| |
| const glw::GLsizei multi_draw_call_count_array[3] = { 3, 6, 3 }; |
| const glw::GLuint* multi_draw_call_indices_array[3] = { (glw::GLuint*)(m_draw_call_index2_offset), |
| (glw::GLuint*)(m_draw_call_index2_offset + 3), |
| (glw::GLuint*)(m_draw_call_index2_offset + 9) }; |
| const glw::GLuint* regular_multi_draw_call_offseted_array[3] = { |
| multi_draw_call_indices_array[0] + offset_from_10_to_0_index, |
| multi_draw_call_indices_array[1] + offset_from_10_to_0_index, |
| multi_draw_call_indices_array[2] + offset_from_10_to_0_index, |
| }; |
| |
| /* Construct the test case descriptor */ |
| _test_case new_test_case; |
| |
| new_test_case.basevertex = basevertex; |
| new_test_case.function_type = (_function_type)n_function; |
| new_test_case.index_offset = m_draw_call_index2_offset; |
| new_test_case.range_start = 10; |
| new_test_case.range_end = 32; |
| new_test_case.index_type = GL_UNSIGNED_INT; |
| new_test_case.primitive_mode = GL_TRIANGLES; |
| new_test_case.regular_draw_call_offset = offset_from_10_to_0_index; |
| new_test_case.should_base_texture_match_reference_texture = true; |
| new_test_case.use_clientside_index_data = use_clientside_index_data; |
| new_test_case.use_clientside_vertex_data = use_clientside_vertex_data; |
| new_test_case.use_geometry_shader_stage = false; |
| new_test_case.use_tessellation_shader_stage = false; |
| new_test_case.use_vertex_attrib_binding = false; |
| |
| memcpy(new_test_case.multi_draw_call_count_array, multi_draw_call_count_array, |
| sizeof(multi_draw_call_count_array)); |
| memcpy(new_test_case.multi_draw_call_indices_array, multi_draw_call_indices_array, |
| sizeof(multi_draw_call_indices_array)); |
| memcpy(new_test_case.regular_multi_draw_call_offseted_array, regular_multi_draw_call_offseted_array, |
| sizeof(regular_multi_draw_call_offseted_array)); |
| |
| m_test_cases.push_back(new_test_case); |
| } /* for (all four functions) */ |
| } /* for (all VAO iterations) */ |
| } |
| |
| /** Executes test iteration. |
| * |
| * @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed. |
| */ |
| tcu::TestNode::IterateResult DrawElementsBaseVertexFunctionalCorrectBaseVertexBehaviorUnderflow::iterate() |
| { |
| setUpTestCases(); |
| executeTestCases(); |
| |
| /* Test case passed */ |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| |
| return STOP; |
| } |
| |
| /** Constructor. |
| * |
| * @param context Rendering context handle. |
| **/ |
| DrawElementsBaseVertexFunctionalCorrectBaseVertexBehaviorOverflow:: |
| DrawElementsBaseVertexFunctionalCorrectBaseVertexBehaviorOverflow(Context& context, const ExtParameters& extParams) |
| : DrawElementsBaseVertexTestBase(context, extParams, "overflow", |
| "Verifies basevertex draw calls work correctly for overflowing " |
| "basevertex values") |
| { |
| /* Left blank on purpose */ |
| } |
| |
| /** Sets up test case descriptors for the test instance. These will later be used |
| * as input for DrawElementsBaseVertexTestBase::executeTestCases(), which performs |
| * the actual testing. |
| **/ |
| void DrawElementsBaseVertexFunctionalCorrectBaseVertexBehaviorOverflow::setUpTestCases() |
| { |
| /* The test needs to be run in two iterations, using client-side memory and buffer object |
| * for index data respectively |
| */ |
| for (int vao_iteration = 0; vao_iteration < 2; ++vao_iteration) |
| { |
| /* Skip client-side vertex array as gl_VertexID is undefined when no buffer bound to ARRAY_BUFFER |
| * See section 11.1.3.9 in OpenGL ES 3.1 spec |
| */ |
| bool use_clientside_vertex_data = 0; |
| bool use_clientside_index_data = ((vao_iteration & (1 << 0)) != 0); |
| |
| /* OpenGL does not support client-side data. */ |
| if (!glu::isContextTypeES(m_context.getRenderContext().getType())) |
| { |
| if (use_clientside_index_data || use_clientside_vertex_data) |
| { |
| continue; |
| } |
| } |
| |
| /* Compute the offsets */ |
| computeVBODataOffsets(use_clientside_index_data, use_clientside_vertex_data); |
| |
| /* We need to test four different functions: |
| * |
| * a) glDrawElementsBaseVertex() (GL) |
| * or glDrawElementsBaseVertexEXT() (ES) |
| * b) glDrawRangeElementsBaseVertex() (GL) |
| * or glDrawRangeElementsBaseVertexEXT() (ES) |
| * c) glDrawElementsInstancedBaseVertex() (GL) |
| * or glDrawElementsInstancedBaseVertexEXT() (ES) |
| * d) glMultiDrawElementsBaseVertex() (GL) |
| * or glMultiDrawElementsBaseVertexEXT() (ES) (if supported) |
| **/ |
| for (int n_function = 0; n_function < FUNCTION_COUNT; ++n_function) |
| { |
| /* Do not try to use the multi draw call if relevant extension is |
| * not supported. */ |
| if (!m_is_ext_multi_draw_arrays_supported && n_function == FUNCTION_GL_MULTI_DRAW_ELEMENTS_BASE_VERTEX) |
| { |
| continue; |
| } |
| |
| const glw::GLenum index_types[] = { GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, GL_UNSIGNED_INT }; |
| |
| const unsigned int n_index_types = sizeof(index_types) / sizeof(index_types[0]); |
| |
| for (unsigned int n_index_type = 0; n_index_type < n_index_types; ++n_index_type) |
| { |
| |
| glw::GLint basevertex = -1; |
| const glw::GLubyte* index_offset = NULL; |
| int index_size = 0; |
| glw::GLenum index_type = index_types[n_index_type]; |
| glw::GLenum regular_draw_call_index_type = 0; |
| int regular_draw_call_index_size = 0; |
| glw::GLboolean use_overflow_test_vertices = false; |
| |
| /* To meet requirements of tests IX.1-IX.2 and IX.3, we need to use two basevertex deltas. */ |
| const glw::GLint basevertex_deltas[] = { |
| 0, /* IX.1-IX.2 */ |
| 1 /* IX.3 */ |
| }; |
| const unsigned int n_basevertex_deltas = sizeof(basevertex_deltas) / sizeof(basevertex_deltas[0]); |
| |
| for (unsigned int n_basevertex_delta = 0; n_basevertex_delta < n_basevertex_deltas; |
| ++n_basevertex_delta) |
| { |
| const glw::GLint basevertex_delta = basevertex_deltas[n_basevertex_delta]; |
| glw::GLuint regular_draw_call_offset = 0; |
| glw::GLuint* regular_draw_call_offset2 = NULL; |
| glw::GLuint range_start = 0; |
| glw::GLuint range_end = 0; |
| bool shouldMatch; |
| |
| switch (index_type) |
| { |
| /* |
| * UBYTE base draw indices: 0, 1, 2 |
| * baseVertex: 256+0 |
| * regular draw indices: 0, 1, 2 |
| * expected result: ubyte indices should not wrap around at 8-bit width, |
| * base draw result should not match regular draw result |
| * |
| * UBYTE base draw indices: 0, 1, 2 |
| * baseVertex: 256+1 |
| * regular draw indices: 257, 258, 259 (uint) |
| * expected result: ubyte indices should be upconverted to 32-bit uint, |
| * base draw result should match regular draw result |
| */ |
| case GL_UNSIGNED_BYTE: |
| { |
| basevertex = 256 + basevertex_delta; |
| index_offset = (const glw::GLubyte*)m_draw_call_index3_offset; |
| index_size = 1; |
| range_start = 0; |
| range_end = 2; |
| regular_draw_call_index_type = (basevertex_delta == 0) ? GL_UNSIGNED_BYTE : GL_UNSIGNED_INT; |
| regular_draw_call_index_size = (regular_draw_call_index_type == GL_UNSIGNED_BYTE) ? 1 : 4; |
| regular_draw_call_offset2 = |
| (basevertex_delta == 0) ? |
| (glw::GLuint*)(m_draw_call_index3_offset) : |
| (glw::GLuint*)(m_draw_call_index5_offset + 12); // 12th in functional5_index_data |
| shouldMatch = (basevertex_delta == 1) ? true : false; |
| use_overflow_test_vertices = true; |
| break; |
| } |
| |
| /* |
| * USHORT base draw indices: 0, 1, 2 |
| * baseVertex: 65536+0 |
| * regular draw indices: 0, 1, 2 |
| * expected result: ubyte indices should not wrap around at 16-bit width, |
| * base draw result should not match regular draw result |
| * |
| * USHORT base draw indices: 0, 1, 2 |
| * baseVertex: 65536+1 |
| * regular draw indices: 65537, 65538, 65539 (uint) |
| * expected result: ushort indices should be upconverted to 32-bit uint, |
| * base draw result should match regular draw result |
| */ |
| case GL_UNSIGNED_SHORT: |
| { |
| basevertex = 65536 + basevertex_delta; |
| index_offset = (const glw::GLubyte*)m_draw_call_index4_offset; |
| index_size = 2; |
| range_start = 0; |
| range_end = 2; |
| regular_draw_call_index_type = (basevertex_delta == 0) ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT; |
| regular_draw_call_index_size = (regular_draw_call_index_type == GL_UNSIGNED_SHORT) ? 2 : 4; |
| regular_draw_call_offset2 = |
| (basevertex_delta == 0) ? |
| (glw::GLuint*)(m_draw_call_index4_offset) : |
| (glw::GLuint*)(m_draw_call_index5_offset + 24); // 24th in functional5_index_data |
| shouldMatch = (basevertex_delta == 1) ? true : false; |
| use_overflow_test_vertices = true; |
| break; |
| } |
| |
| /* |
| * UINT base draw indices: 2^31+2, 2^31+3, 2^31+4 |
| * baseVertex: 2^31-2 |
| * regular draw indices: 0, 1, 2 |
| * expected result: uint indices should wrap to {0, 1, 2}, |
| * base draw result should match regular draw result |
| * |
| * UINT base draw indices: 2^31+2, 2^31+3, 2^31+4 |
| * baseVertex: 2^31-1 |
| * regular draw indices: 0, 1, 2 |
| * expected result: uint indices should wrap to {1, 2, 3}, |
| * base draw result should not match regular draw result |
| */ |
| case GL_UNSIGNED_INT: |
| { |
| basevertex = 2147483647 - 1 + basevertex_delta; |
| index_offset = (const glw::GLubyte*)m_draw_call_index5_offset; |
| index_size = 4; |
| range_start = 2147483647 + 3u; // 2^31+2 |
| range_end = 2147483647 + 5u; // 2^31+4 |
| regular_draw_call_index_type = GL_UNSIGNED_INT; |
| regular_draw_call_index_size = 4; |
| regular_draw_call_offset = 36; // 36th in functional5_index_data |
| shouldMatch = (basevertex_delta == 0) ? true : false; |
| use_overflow_test_vertices = false; |
| break; |
| } |
| |
| default: |
| { |
| TCU_FAIL("Unrecognized index type"); |
| } |
| } /* switch (index_type) */ |
| |
| /* Prepare a few arrays so that we can handle the multi draw call and its emulated version.. */ |
| const glw::GLsizei multi_draw_call_count_array[3] = { 3, 6, 3 }; |
| const glw::GLubyte* multi_draw_call_indices_array[3] = { index_offset, |
| index_offset + 3 * index_size, |
| index_offset + 9 * index_size }; |
| const glw::GLubyte* regular_multi_draw_call_offseted_array[3]; |
| if (use_overflow_test_vertices) |
| { |
| regular_multi_draw_call_offseted_array[0] = (glw::GLubyte*)regular_draw_call_offset2; |
| regular_multi_draw_call_offseted_array[1] = |
| (glw::GLubyte*)regular_draw_call_offset2 + 3 * regular_draw_call_index_size; |
| regular_multi_draw_call_offseted_array[2] = |
| (glw::GLubyte*)regular_draw_call_offset2 + 9 * regular_draw_call_index_size; |
| } |
| else |
| { |
| regular_multi_draw_call_offseted_array[0] = |
| multi_draw_call_indices_array[0] + index_size * regular_draw_call_offset; |
| regular_multi_draw_call_offseted_array[1] = |
| multi_draw_call_indices_array[1] + index_size * regular_draw_call_offset; |
| regular_multi_draw_call_offseted_array[2] = |
| multi_draw_call_indices_array[2] + index_size * regular_draw_call_offset; |
| } |
| |
| /* Construct the test case descriptor */ |
| _test_case new_test_case; |
| |
| new_test_case.basevertex = basevertex; |
| new_test_case.function_type = (_function_type)n_function; |
| new_test_case.index_offset = (const glw::GLuint*)index_offset; |
| new_test_case.range_start = range_start; |
| new_test_case.range_end = range_end; |
| new_test_case.index_type = index_type; |
| new_test_case.primitive_mode = GL_TRIANGLES; |
| new_test_case.regular_draw_call_offset = regular_draw_call_offset; |
| new_test_case.regular_draw_call_offset2 = regular_draw_call_offset2; |
| new_test_case.regular_draw_call_index_type = regular_draw_call_index_type; |
| new_test_case.should_base_texture_match_reference_texture = shouldMatch; |
| new_test_case.use_clientside_index_data = use_clientside_index_data; |
| new_test_case.use_clientside_vertex_data = use_clientside_vertex_data; |
| new_test_case.use_geometry_shader_stage = false; |
| new_test_case.use_tessellation_shader_stage = false; |
| new_test_case.use_vertex_attrib_binding = false; |
| new_test_case.use_overflow_test_vertices = use_overflow_test_vertices != GL_FALSE; |
| |
| memcpy(new_test_case.multi_draw_call_count_array, multi_draw_call_count_array, |
| sizeof(multi_draw_call_count_array)); |
| memcpy(new_test_case.multi_draw_call_indices_array, multi_draw_call_indices_array, |
| sizeof(multi_draw_call_indices_array)); |
| memcpy(new_test_case.regular_multi_draw_call_offseted_array, regular_multi_draw_call_offseted_array, |
| sizeof(regular_multi_draw_call_offseted_array)); |
| |
| m_test_cases.push_back(new_test_case); |
| } /* for (all basevertex deltas) */ |
| } /* for (all index types) */ |
| } /* for (all four functions) */ |
| } /* for (all VAO iterations) */ |
| } |
| |
| /** Executes test iteration. |
| * |
| * @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed. |
| */ |
| tcu::TestNode::IterateResult DrawElementsBaseVertexFunctionalCorrectBaseVertexBehaviorOverflow::iterate() |
| { |
| setUpTestCases(); |
| executeTestCases(); |
| |
| /* Test case passed */ |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| |
| return STOP; |
| } |
| |
| /** Constructor. |
| * |
| * @param context Rendering context handle. |
| **/ |
| DrawElementsBaseVertexFunctionalCorrectBaseVertexBehaviorAEPShaderStages:: |
| DrawElementsBaseVertexFunctionalCorrectBaseVertexBehaviorAEPShaderStages(Context& context, |
| const ExtParameters& extParams) |
| : DrawElementsBaseVertexTestBase(context, extParams, "AEP_shader_stages", |
| "Verifies basevertex draw calls work correctly when geometry & " |
| "tessellation shader stages are used in the rendering pipeline.") |
| { |
| /* Left blank intentionally */ |
| } |
| |
| /** Sets up test case descriptors for the test instance. These will later be used |
| * as input for DrawElementsBaseVertexTestBase::executeTestCases(), which performs |
| * the actual testing. |
| **/ |
| void DrawElementsBaseVertexFunctionalCorrectBaseVertexBehaviorAEPShaderStages::setUpTestCases() |
| { |
| /* Set up test case descriptors */ |
| const glw::GLuint basevertex_values[] = { |
| 10, /* VI.1-VI.4 */ |
| 0 /* VI.5 */ |
| }; |
| const unsigned int n_basevertex_values = sizeof(basevertex_values) / sizeof(basevertex_values[0]); |
| |
| /* The test needs to be run in two iterations, using client-side memory and buffer object |
| * for index data respectively |
| */ |
| for (int vao_iteration = 0; vao_iteration < 2; ++vao_iteration) |
| { |
| /* Skip client-side vertex array as gl_VertexID is undefined when zero buffer is bound to ARRAY_BUFFER */ |
| bool use_clientside_vertex_data = 0; |
| bool use_clientside_index_data = ((vao_iteration & (1 << 0)) != 0); |
| |
| /* OpenGL does not support client-side data. */ |
| if (!glu::isContextTypeES(m_context.getRenderContext().getType())) |
| { |
| if (use_clientside_index_data || use_clientside_vertex_data) |
| { |
| continue; |
| } |
| } |
| |
| /* Compute the offsets */ |
| computeVBODataOffsets(use_clientside_index_data, use_clientside_vertex_data); |
| |
| /* There are two index data sets we need to iterate over */ |
| const glw::GLuint* index_offsets[] = { m_draw_call_index_offset, m_draw_call_index2_offset }; |
| const unsigned int n_index_offsets = sizeof(index_offsets) / sizeof(index_offsets[0]); |
| |
| for (unsigned int n_index_offset = 0; n_index_offset < n_index_offsets; ++n_index_offset) |
| { |
| const glw::GLuint* current_index_offset = index_offsets[n_index_offset]; |
| |
| /* We need to test four different functions: |
| * |
| * a) glDrawElementsBaseVertex() (GL) |
| * or glDrawElementsBaseVertexEXT() (ES) |
| * b) glDrawRangeElementsBaseVertex() (GL) |
| * or glDrawRangeElementsBaseVertexEXT() (ES) |
| * c) glDrawElementsInstancedBaseVertex() (GL) |
| * or glDrawElementsInstancedBaseVertexEXT() (ES) |
| * d) glMultiDrawElementsBaseVertex() (GL) |
| * or glMultiDrawElementsBaseVertexEXT() (ES) (if supported) |
| **/ |
| for (int n_function = 0; n_function < FUNCTION_COUNT; ++n_function) |
| { |
| /* Iterate over all basevertex values */ |
| for (unsigned int n_basevertex_value = 0; n_basevertex_value < n_basevertex_values; |
| ++n_basevertex_value) |
| { |
| |
| /* Iterate over all GS+(TC & TE) stage combinations */ |
| for (unsigned int n_stage_combination = 0; n_stage_combination < 4; ++n_stage_combination) |
| { |
| bool should_include_gs = (n_stage_combination & 1) != 0; |
| bool should_include_tc_te = (n_stage_combination & 2) != 0; |
| |
| /* Skip iterations, for which we'd need to use extensions not supported |
| * by the running implementation */ |
| if (should_include_gs && !m_is_geometry_shader_supported) |
| { |
| continue; |
| } |
| |
| if (should_include_tc_te && !m_is_tessellation_shader_supported) |
| { |
| continue; |
| } |
| |
| /* Do not try to use the multi draw call if relevant extension is |
| * not supported. */ |
| if (!m_is_ext_multi_draw_arrays_supported && |
| n_function == FUNCTION_GL_MULTI_DRAW_ELEMENTS_BASE_VERTEX) |
| { |
| continue; |
| } |
| |
| /* Prepare a few arrays so that we can handle the multi draw call and its emulated version.. */ |
| const glw::GLsizei multi_draw_call_count_array[3] = { 3, 6, 3 }; |
| const glw::GLuint* multi_draw_call_indices_array[3] = { |
| (glw::GLuint*)(current_index_offset), (glw::GLuint*)(current_index_offset + 3), |
| (glw::GLuint*)(current_index_offset + 9) |
| }; |
| |
| /* Reference texture should always reflect basevertex=10 behavior. */ |
| const glw::GLuint regular_draw_call_offset = basevertex_values[0]; |
| const glw::GLuint* regular_multi_draw_call_offseted_array[3] = { |
| multi_draw_call_indices_array[0] + regular_draw_call_offset, |
| multi_draw_call_indices_array[1] + regular_draw_call_offset, |
| multi_draw_call_indices_array[2] + regular_draw_call_offset, |
| }; |
| |
| /* Construct the test case descriptor */ |
| _test_case new_test_case; |
| |
| new_test_case.basevertex = basevertex_values[n_basevertex_value]; |
| new_test_case.function_type = (_function_type)n_function; |
| new_test_case.index_offset = current_index_offset; |
| new_test_case.range_start = n_index_offset == 0 ? 0 : 10; |
| new_test_case.range_end = n_index_offset == 0 ? 22 : 32; |
| new_test_case.index_type = GL_UNSIGNED_INT; |
| new_test_case.primitive_mode = (should_include_tc_te) ? GL_PATCHES : GL_TRIANGLES; |
| new_test_case.regular_draw_call_offset = regular_draw_call_offset; |
| new_test_case.should_base_texture_match_reference_texture = |
| ((glw::GLuint)new_test_case.basevertex == new_test_case.regular_draw_call_offset); |
| new_test_case.use_clientside_index_data = use_clientside_index_data; |
| new_test_case.use_clientside_vertex_data = use_clientside_vertex_data; |
| new_test_case.use_geometry_shader_stage = should_include_gs; |
| new_test_case.use_tessellation_shader_stage = should_include_tc_te; |
| new_test_case.use_vertex_attrib_binding = false; |
| |
| memcpy(new_test_case.multi_draw_call_count_array, multi_draw_call_count_array, |
| sizeof(multi_draw_call_count_array)); |
| memcpy(new_test_case.multi_draw_call_indices_array, multi_draw_call_indices_array, |
| sizeof(multi_draw_call_indices_array)); |
| memcpy(new_test_case.regular_multi_draw_call_offseted_array, |
| regular_multi_draw_call_offseted_array, sizeof(regular_multi_draw_call_offseted_array)); |
| |
| m_test_cases.push_back(new_test_case); |
| } /* for (all shader stage combinations) */ |
| } /* for (all basevertex values) */ |
| } /* for (all four functions) */ |
| } /* for (all index data sets) */ |
| } /* for (all VAO iterations) */ |
| } |
| |
| /** Executes test iteration. |
| * |
| * @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed. |
| */ |
| tcu::TestNode::IterateResult DrawElementsBaseVertexFunctionalCorrectBaseVertexBehaviorAEPShaderStages::iterate() |
| { |
| /* This test should not be run on implementations that don't support both tessellation and geometry |
| * shader stages |
| */ |
| if (!m_is_geometry_shader_supported && !m_is_tessellation_shader_supported) |
| { |
| throw tcu::NotSupportedError("Geometry shader and tessellation shader functionality is not supported"); |
| } |
| |
| setUpTestCases(); |
| executeTestCases(); |
| |
| /* Test case passed */ |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| |
| return STOP; |
| } |
| |
| /** Constructor. |
| * |
| * @param context Rendering context handle. |
| **/ |
| DrawElementsBaseVertexNegativeActiveTransformFeedbackTest::DrawElementsBaseVertexNegativeActiveTransformFeedbackTest( |
| Context& context, const ExtParameters& extParams) |
| : DrawElementsBaseVertexTestBase(context, extParams, "valid_active_tf", |
| "Tries to do \"base vertex\" draw calls while Transform Feedback is active") |
| , m_bo_tf_result_id(0) |
| { |
| /* Left blank on purpose */ |
| } |
| |
| /** Deinitializes all ES objects per test case if test fails and exit through exception path */ |
| void DrawElementsBaseVertexNegativeActiveTransformFeedbackTest::deinit() |
| { |
| deinitPerTestObjects(); |
| } |
| |
| /** Deinitializes all ES objects that may have been created by the test */ |
| void DrawElementsBaseVertexNegativeActiveTransformFeedbackTest::deinitPerTestObjects() |
| { |
| /* Call the base class' deinitPerTestObjects() first. */ |
| DrawElementsBaseVertexTestBase::deinitPerTestObjects(); |
| |
| /* Proceed with internal deinitialization */ |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| if (m_bo_tf_result_id != 0) |
| { |
| gl.deleteBuffers(1, &m_bo_tf_result_id); |
| |
| m_bo_tf_result_id = 0; |
| } |
| } |
| |
| /** Initializes all ES objects used by the test. */ |
| void DrawElementsBaseVertexNegativeActiveTransformFeedbackTest::init() |
| { |
| /* Call the base class' init() first. */ |
| DrawElementsBaseVertexTestBase::init(); |
| |
| /* Proceed with internal initialization */ |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| gl.genBuffers(1, &m_bo_tf_result_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers() call failed"); |
| |
| gl.bindBuffer(GL_ARRAY_BUFFER, m_bo_tf_result_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call failed"); |
| |
| gl.bufferData(GL_ARRAY_BUFFER, 3 /* count */ * sizeof(float) * 4 /* components used by gl_Position */, |
| DE_NULL, /* data */ |
| GL_STATIC_DRAW); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() call failed."); |
| |
| gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, /* index */ |
| m_bo_tf_result_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase() call failed"); |
| } |
| |
| /** Executes test iteration. |
| * |
| * @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed. |
| */ |
| tcu::TestNode::IterateResult DrawElementsBaseVertexNegativeActiveTransformFeedbackTest::iterate() |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| /* this test doesn't apply to OpenGL contexts */ |
| if (!glu::isContextTypeES(m_context.getRenderContext().getType())) |
| { |
| m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not Supported"); |
| return STOP; |
| } |
| |
| /* Set up the work environment */ |
| setUpNegativeTestObjects(false, /* use_clientside_vertex_data */ |
| false); /* use_clientside_index_data */ |
| |
| /* Kick off transform feedback */ |
| gl.beginTransformFeedback(GL_TRIANGLES); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback() call failed."); |
| |
| /* Try to perform indiced draw calls which are invalid when TF is active. |
| */ |
| glw::GLenum error_code = GL_NONE; |
| |
| gl.drawElementsBaseVertex(GL_TRIANGLES, 3, /* count */ |
| GL_UNSIGNED_INT, m_draw_call_index_offset, 0); /* basevertex */ |
| |
| error_code = gl.getError(); |
| |
| /* The error for using DrawElements* commands while transform feedback is |
| active is lifted in OES/EXT_geometry_shader. See issue 13 in the spec. |
| */ |
| glw::GLenum expected_error_code = m_is_geometry_shader_supported ? GL_NO_ERROR : GL_INVALID_OPERATION; |
| |
| if (error_code != expected_error_code) |
| { |
| std::stringstream error_sstream; |
| |
| error_sstream << "Invalid error code generated by " << getFunctionName(FUNCTION_GL_DRAW_ELEMENTS_BASE_VERTEX); |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << getFunctionName(FUNCTION_GL_DRAW_ELEMENTS_BASE_VERTEX) |
| << " returned error code [" << error_code << "] instead of [" << expected_error_code << "]." |
| << tcu::TestLog::EndMessage; |
| |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, error_sstream.str().c_str()); |
| |
| goto end; |
| } |
| |
| gl.drawElementsInstancedBaseVertex(GL_TRIANGLES, 3, /* count */ |
| GL_UNSIGNED_INT, m_draw_call_index_offset, 1, /* instancecount */ |
| 0); /* basevertex */ |
| |
| error_code = gl.getError(); |
| if (error_code != expected_error_code) |
| { |
| std::stringstream error_sstream; |
| |
| error_sstream << "Invalid error code generated by " |
| << getFunctionName(FUNCTION_GL_DRAW_ELEMENTS_INSTANCED_BASE_VERTEX); |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << getFunctionName(FUNCTION_GL_DRAW_ELEMENTS_INSTANCED_BASE_VERTEX) |
| << " returned error code [" << error_code << "] instead of [" << expected_error_code << "]." |
| << tcu::TestLog::EndMessage; |
| |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, error_sstream.str().c_str()); |
| |
| goto end; |
| } |
| |
| gl.drawRangeElementsBaseVertex(GL_TRIANGLES, 0, 2, /* end */ |
| 3, /* count */ |
| GL_UNSIGNED_INT, m_draw_call_index_offset, 0); /* basevertex */ |
| |
| error_code = gl.getError(); |
| if (error_code != expected_error_code) |
| { |
| std::stringstream error_sstream; |
| |
| error_sstream << "Invalid error code generated by " |
| << getFunctionName(FUNCTION_GL_DRAW_RANGE_ELEMENTS_BASE_VERTEX); |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << getFunctionName(FUNCTION_GL_DRAW_RANGE_ELEMENTS_BASE_VERTEX) |
| << " returned error code [" << error_code << "] instead of [" << expected_error_code << "]." |
| << tcu::TestLog::EndMessage; |
| |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, error_sstream.str().c_str()); |
| |
| goto end; |
| } |
| |
| /* Test case passed */ |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| |
| end: |
| gl.endTransformFeedback(); |
| return STOP; |
| } |
| |
| /** Constructor. |
| * |
| * @param context Rendering context handle. |
| **/ |
| DrawElementsBaseVertexNegativeInvalidCountArgumentTest::DrawElementsBaseVertexNegativeInvalidCountArgumentTest( |
| Context& context, const ExtParameters& extParams) |
| : DrawElementsBaseVertexTestBase(context, extParams, "invalid_count_argument", |
| "Tries to use invalid 'count' argument values for the \"base vertex\" draw calls") |
| { |
| /* Left blank on purpose */ |
| } |
| |
| /** Executes test iteration. |
| * |
| * @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed. |
| */ |
| tcu::TestNode::IterateResult DrawElementsBaseVertexNegativeInvalidCountArgumentTest::iterate() |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| /* The test needs to be run in four iterations, where for each iteration we configure the VAO |
| * in a slightly different manner. |
| */ |
| for (int iteration = 0; iteration < 4; ++iteration) |
| { |
| bool use_clientside_index_data = ((iteration & (1 << 0)) != 0); |
| bool use_clientside_vertex_data = ((iteration & (1 << 1)) != 0); |
| |
| /* OpenGL does not support client-side data. */ |
| if (!glu::isContextTypeES(m_context.getRenderContext().getType())) |
| { |
| if (use_clientside_index_data || use_clientside_vertex_data) |
| { |
| continue; |
| } |
| } |
| |
| /* Set up the work environment */ |
| setUpNegativeTestObjects(use_clientside_vertex_data, use_clientside_index_data); |
| |
| /* Try to execute the invalid draw calls */ |
| glw::GLenum error_code = GL_NONE; |
| |
| gl.drawElementsBaseVertex(GL_TRIANGLES, -1, /* count */ |
| GL_UNSIGNED_INT, (const glw::GLvoid*)m_draw_call_index_offset, 0); /* basevertex */ |
| |
| error_code = gl.getError(); |
| if (error_code != GL_INVALID_VALUE) |
| { |
| std::stringstream error_sstream; |
| |
| error_sstream << "Invalid error code reported for an invalid " |
| << getFunctionName(FUNCTION_GL_DRAW_ELEMENTS_BASE_VERTEX) << " call"; |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << error_sstream.str().c_str() |
| << ": expected GL_INVALID_VALUE, got:" |
| "[" |
| << error_code << "]" << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL(error_sstream.str().c_str()); |
| } |
| |
| gl.drawRangeElementsBaseVertex(GL_TRIANGLES, /* mode */ |
| -1, /* start */ |
| 2, /* end */ |
| 1, /* count */ |
| GL_UNSIGNED_INT, (const glw::GLvoid*)m_draw_call_index_offset, |
| 0); /* basevertex */ |
| |
| error_code = gl.getError(); |
| if (error_code != GL_INVALID_VALUE) |
| { |
| std::stringstream error_sstream; |
| |
| error_sstream << "Invalid error code reported for an invalid " |
| << getFunctionName(FUNCTION_GL_DRAW_RANGE_ELEMENTS_BASE_VERTEX) << " call"; |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << error_sstream.str().c_str() |
| << ": expected GL_INVALID_VALUE, got:" |
| "[" |
| << error_code << "]" << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL(error_sstream.str().c_str()); |
| } |
| |
| gl.drawElementsInstancedBaseVertex(GL_TRIANGLES, /* mode */ |
| -1, /* count */ |
| GL_UNSIGNED_INT, (const glw::GLvoid*)m_draw_call_index_offset, |
| 1, /* instancecount */ |
| 0); /* basevertex */ |
| |
| error_code = gl.getError(); |
| if (error_code != GL_INVALID_VALUE) |
| { |
| std::stringstream error_sstream; |
| |
| error_sstream << "Invalid error code reported for an invalid " |
| << getFunctionName(FUNCTION_GL_DRAW_ELEMENTS_INSTANCED_BASE_VERTEX) << " call"; |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << error_sstream.str().c_str() |
| << ": expected GL_INVALID_VALUE, got:" |
| "[" |
| << error_code << "]" << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL(error_sstream.str().c_str()); |
| } |
| |
| if (m_is_ext_multi_draw_arrays_supported) |
| { |
| const glw::GLsizei count = -1; |
| const glw::GLvoid* offsets[] = { (const glw::GLvoid*)m_draw_call_index_offset }; |
| |
| gl.multiDrawElementsBaseVertex(GL_TRIANGLES, /* mode */ |
| &count, GL_UNSIGNED_INT, offsets, 1, /* primcount */ |
| 0); /* basevertex */ |
| |
| error_code = gl.getError(); |
| if (error_code != GL_INVALID_VALUE) |
| { |
| std::stringstream error_sstream; |
| |
| error_sstream << "Invalid error code reported for an invalid " |
| << getFunctionName(FUNCTION_GL_MULTI_DRAW_ELEMENTS_BASE_VERTEX) << " call"; |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << error_sstream.str().c_str() |
| << ": expected GL_INVALID_VALUE, got:" |
| "[" |
| << error_code << "]" << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL("Invalid error code reported for an invalid glMultiDrawElementsBaseVertexEXT() call."); |
| } |
| } /* if (m_is_ext_multi_draw_arrays_supported) */ |
| } /* for (all iterations) */ |
| |
| /* Test case passed */ |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| |
| return STOP; |
| } |
| |
| /** Constructor. |
| * |
| * @param context Rendering context handle. |
| **/ |
| DrawElementsBaseVertexNegativeInvalidInstanceCountArgumentTest:: |
| DrawElementsBaseVertexNegativeInvalidInstanceCountArgumentTest(Context& context, const ExtParameters& extParams) |
| : DrawElementsBaseVertexTestBase(context, extParams, "invalid_instancecount_argument", |
| "Tries to use invalid 'instancecount' argument values for " |
| "glDrawElementsInstancedBaseVertexEXT (ES) or " |
| "glDrawElementsInstancedBaseVertex (GL) draw call") |
| { |
| /* Left blank on purpose */ |
| } |
| |
| /** Executes test iteration. |
| * |
| * @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed. |
| */ |
| tcu::TestNode::IterateResult DrawElementsBaseVertexNegativeInvalidInstanceCountArgumentTest::iterate() |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| /* The test needs to be run in four iterations, where for each iteration we configure the VAO |
| * in a slightly different manner. |
| */ |
| for (int iteration = 0; iteration < 4; ++iteration) |
| { |
| bool use_clientside_index_data = ((iteration & (1 << 0)) != 0); |
| bool use_clientside_vertex_data = ((iteration & (1 << 1)) != 0); |
| |
| /* OpenGL does not support client-side data. */ |
| if (!glu::isContextTypeES(m_context.getRenderContext().getType())) |
| { |
| if (use_clientside_index_data || use_clientside_vertex_data) |
| { |
| continue; |
| } |
| } |
| |
| /* Set up the work environment */ |
| setUpNegativeTestObjects(use_clientside_vertex_data, use_clientside_index_data); |
| |
| /* Try to execute the invalid draw call */ |
| glw::GLenum error_code = GL_NONE; |
| |
| gl.drawElementsInstancedBaseVertex(GL_TRIANGLES, /* mode */ |
| 3, /* count */ |
| GL_UNSIGNED_INT, (const glw::GLvoid*)m_draw_call_index_offset, |
| -1, /* instancecount */ |
| 0); /* basevertex */ |
| |
| error_code = gl.getError(); |
| if (error_code != GL_INVALID_VALUE) |
| { |
| std::stringstream error_sstream; |
| |
| error_sstream << "Invalid error code reported for an invalid " |
| << getFunctionName(FUNCTION_GL_DRAW_ELEMENTS_INSTANCED_BASE_VERTEX) << " call"; |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << error_sstream.str().c_str() |
| << ": expected GL_INVALID_VALUE, got:" |
| "[" |
| << error_code << "]" << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL(error_sstream.str().c_str()); |
| } |
| } /* for (all test iterations) */ |
| |
| /* Test case passed */ |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| |
| return STOP; |
| } |
| |
| /** Constructor. |
| * |
| * @param context Rendering context handle. |
| **/ |
| DrawElementsBaseVertexNegativeInvalidModeArgumentTest::DrawElementsBaseVertexNegativeInvalidModeArgumentTest( |
| Context& context, const ExtParameters& extParams) |
| : DrawElementsBaseVertexTestBase(context, extParams, "invalid_mode_argument", |
| "Tries to use invalid 'mode' argument values for the \"base vertex\" draw calls") |
| { |
| /* Left blank on purpose */ |
| } |
| |
| /** Executes test iteration. |
| * |
| * @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed. |
| */ |
| tcu::TestNode::IterateResult DrawElementsBaseVertexNegativeInvalidModeArgumentTest::iterate() |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| /* The test needs to be run in four iterations, where for each iteration we configure the VAO |
| * in a slightly different manner. |
| */ |
| for (int iteration = 0; iteration < 4; ++iteration) |
| { |
| bool use_clientside_index_data = ((iteration & (1 << 0)) != 0); |
| bool use_clientside_vertex_data = ((iteration & (1 << 1)) != 0); |
| |
| /* OpenGL does not support client-side data. */ |
| if (!glu::isContextTypeES(m_context.getRenderContext().getType())) |
| { |
| if (use_clientside_index_data || use_clientside_vertex_data) |
| { |
| continue; |
| } |
| } |
| |
| /* Set up the work environment */ |
| setUpNegativeTestObjects(use_clientside_vertex_data, use_clientside_index_data); |
| |
| /* Try to execute the invalid draw calls */ |
| glw::GLenum error_code = GL_NONE; |
| |
| gl.drawElementsBaseVertex(GL_GREATER, /* mode */ |
| 3, /* count */ |
| GL_UNSIGNED_INT, (const glw::GLvoid*)m_draw_call_index_offset, 0); /* basevertex */ |
| |
| error_code = gl.getError(); |
| if (error_code != GL_INVALID_ENUM) |
| { |
| std::stringstream error_sstream; |
| |
| error_sstream << "Invalid error code reported for an invalid " |
| << getFunctionName(FUNCTION_GL_DRAW_ELEMENTS_BASE_VERTEX) << " call"; |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << error_sstream.str().c_str() |
| << ": expected GL_INVALID_ENUM, got:" |
| "[" |
| << error_code << "]" << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL(error_sstream.str().c_str()); |
| } |
| |
| gl.drawRangeElementsBaseVertex(GL_GREATER, /* mode */ |
| 0, /* start */ |
| 2, /* end */ |
| 3, /* count */ |
| GL_UNSIGNED_INT, (const glw::GLvoid*)m_draw_call_index_offset, |
| 0); /* basevertex */ |
| |
| error_code = gl.getError(); |
| if (error_code != GL_INVALID_ENUM) |
| { |
| std::stringstream error_sstream; |
| |
| error_sstream << "Invalid error code reported for an invalid " |
| << getFunctionName(FUNCTION_GL_DRAW_RANGE_ELEMENTS_BASE_VERTEX) << " call"; |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << error_sstream.str().c_str() |
| << ": expected GL_INVALID_ENUM, got:" |
| "[" |
| << error_code << "]" << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL(error_sstream.str().c_str()); |
| } |
| |
| gl.drawElementsInstancedBaseVertex(GL_GREATER, /* mode */ |
| 3, /* count */ |
| GL_UNSIGNED_INT, (const glw::GLvoid*)m_draw_call_index_offset, |
| 1, /* instancecount */ |
| 0); /* basevertex */ |
| |
| error_code = gl.getError(); |
| if (error_code != GL_INVALID_ENUM) |
| { |
| std::stringstream error_sstream; |
| |
| error_sstream << "Invalid error code reported for an invalid " |
| << getFunctionName(FUNCTION_GL_DRAW_ELEMENTS_INSTANCED_BASE_VERTEX) << " call"; |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << error_sstream.str().c_str() |
| << ": expected GL_INVALID_ENUM, got:" |
| "[" |
| << error_code << "]" << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL(error_sstream.str().c_str()); |
| } |
| |
| if (m_is_ext_multi_draw_arrays_supported) |
| { |
| const glw::GLsizei count = 3; |
| const glw::GLvoid* offsets[] = { (const glw::GLvoid*)m_draw_call_index_offset }; |
| |
| gl.multiDrawElementsBaseVertex(GL_GREATER, /* mode */ |
| &count, GL_UNSIGNED_INT, offsets, 1, /* primcount */ |
| 0); /* basevertex */ |
| |
| error_code = gl.getError(); |
| if (error_code != GL_INVALID_ENUM) |
| { |
| std::stringstream error_sstream; |
| |
| error_sstream << "Invalid error code reported for an invalid " |
| << getFunctionName(FUNCTION_GL_MULTI_DRAW_ELEMENTS_BASE_VERTEX) << " call"; |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << error_sstream.str().c_str() |
| << ": expected GL_INVALID_ENUM, got:" |
| "[" |
| << error_code << "]" << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL(error_sstream.str().c_str()); |
| } |
| } /* if (m_is_ext_multi_draw_arrays_supported) */ |
| } /* for (all test iterations) */ |
| |
| /* Test case passed */ |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| |
| return STOP; |
| } |
| |
| /** Constructor. |
| * |
| * @param context Rendering context handle. |
| **/ |
| DrawElementsBaseVertexNegativeInvalidPrimcountArgumentTest::DrawElementsBaseVertexNegativeInvalidPrimcountArgumentTest( |
| Context& context, const ExtParameters& extParams) |
| : DrawElementsBaseVertexTestBase( |
| context, extParams, "invalid_primcount_argument", |
| "Tries to use invalid 'primcount' argument values for the \"base vertex\" draw calls") |
| { |
| /* Left blank on purpose */ |
| } |
| |
| /** Executes test iteration. |
| * |
| * @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed. |
| */ |
| tcu::TestNode::IterateResult DrawElementsBaseVertexNegativeInvalidPrimcountArgumentTest::iterate() |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| /* This test requires presence of GL_EXT_multi_draw_arrays under ES contexts */ |
| if (glu::isContextTypeES(m_context.getRenderContext().getType())) |
| { |
| if (!m_is_ext_multi_draw_arrays_supported) |
| { |
| throw tcu::NotSupportedError("GL_EXT_multi_draw_arrays is not supported"); |
| } |
| } |
| |
| /* The test needs to be run in four iterations, where for each iteration we configure the VAO |
| * in a slightly different manner. |
| */ |
| for (int iteration = 0; iteration < 4 |