/*------------------------------------------------------------------------- * OpenGL Conformance Test Suite * ----------------------------- * * Copyright (c) 2015-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 gl4cDirectStateAccessVertexArraysTests.cpp * \brief Conformance tests for the Direct State Access feature functionality (Vertex Array Objects access part). */ /*-----------------------------------------------------------------------------------------------------------*/ /* Includes. */ #include "gl4cDirectStateAccessTests.hpp" #include "deSharedPtr.hpp" #include "gluContextInfo.hpp" #include "gluDefs.hpp" #include "gluPixelTransfer.hpp" #include "gluStrUtil.hpp" #include "tcuFuzzyImageCompare.hpp" #include "tcuImageCompare.hpp" #include "tcuRenderTarget.hpp" #include "tcuSurface.hpp" #include "tcuTestLog.hpp" #include "glw.h" #include "glwFunctions.hpp" #include <algorithm> #include <climits> #include <cmath> #include <set> #include <sstream> #include <stack> namespace gl4cts { namespace DirectStateAccess { namespace VertexArrays { /******************************** Creation Test Implementation ********************************/ /** @brief Creation Test constructor. * * @param [in] context OpenGL context. */ CreationTest::CreationTest(deqp::Context& context) : deqp::TestCase(context, "vertex_arrays_creation", "Vertex Array Objects Creation Test") { /* Intentionally left blank. */ } /** @brief Iterate Creation Test cases. * * @return Iteration result. */ tcu::TestNode::IterateResult CreationTest::iterate() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Get context setup. */ bool is_at_least_gl_45 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5))); bool is_arb_direct_state_access = m_context.getContextInfo().isExtensionSupported("GL_ARB_direct_state_access"); if ((!is_at_least_gl_45) && (!is_arb_direct_state_access)) { m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not Supported"); return STOP; } /* Running tests. */ bool is_ok = true; bool is_error = false; /* VertexArrays' objects */ static const glw::GLuint vertex_arrays_count = 2; glw::GLuint vertex_arrays_legacy[vertex_arrays_count] = {}; glw::GLuint vertex_arrays_dsa[vertex_arrays_count] = {}; try { /* Check legacy state creation. */ gl.genVertexArrays(vertex_arrays_count, vertex_arrays_legacy); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenVertexArrays have failed"); for (glw::GLuint i = 0; i < vertex_arrays_count; ++i) { if (gl.isVertexArray(vertex_arrays_legacy[i])) { is_ok = false; /* Log. */ m_context.getTestContext().getLog() << tcu::TestLog::Message << "GenVertexArrays has created default objects, but it should create only a names." << tcu::TestLog::EndMessage; } } /* Check direct state creation. */ gl.createVertexArrays(vertex_arrays_count, vertex_arrays_dsa); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateVertexArrays have failed"); for (glw::GLuint i = 0; i < vertex_arrays_count; ++i) { if (!gl.isVertexArray(vertex_arrays_dsa[i])) { is_ok = false; /* Log. */ m_context.getTestContext().getLog() << tcu::TestLog::Message << "CreateVertexArrays has not created default objects." << tcu::TestLog::EndMessage; } } } catch (...) { is_ok = false; is_error = true; } /* Cleanup. */ for (glw::GLuint i = 0; i < vertex_arrays_count; ++i) { if (vertex_arrays_legacy[i]) { gl.deleteVertexArrays(1, &vertex_arrays_legacy[i]); vertex_arrays_legacy[i] = 0; } if (vertex_arrays_dsa[i]) { gl.deleteVertexArrays(1, &vertex_arrays_dsa[i]); vertex_arrays_dsa[i] = 0; } } /* Errors clean up. */ while (gl.getError()) ; /* Result's setup. */ if (is_ok) { m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (is_error) { m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); } else { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } return STOP; } /******************************** Vertex Array Object Enable Disable Attributes Test Implementation ********************************/ /** @brief Vertex Array Object Enable Disable Attributes Test constructor. * * @param [in] context OpenGL context. */ EnableDisableAttributesTest::EnableDisableAttributesTest(deqp::Context& context) : deqp::TestCase(context, "vertex_arrays_enable_disable_attributes", "Vertex Array Objects Enable Disable Attributes Test") , m_po_even(0) , m_po_odd(0) , m_vao(0) , m_bo(0) , m_bo_xfb(0) , m_max_attributes(16) /* Required Minimum: OpenGL 4.5 Table 23.57: Implementation Dependent Vertex Shader Limits */ { /* Intentionally left blank. */ } /** @brief Iterate Vertex Array Object Enable Disable Attributes Test cases. * * @return Iteration result. */ tcu::TestNode::IterateResult EnableDisableAttributesTest::iterate() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Get context setup. */ bool is_at_least_gl_45 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5))); bool is_arb_direct_state_access = m_context.getContextInfo().isExtensionSupported("GL_ARB_direct_state_access"); if ((!is_at_least_gl_45) && (!is_arb_direct_state_access)) { m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not Supported"); return STOP; } /* Running tests. */ bool is_ok = true; bool is_error = false; try { gl.getIntegerv(GL_MAX_VERTEX_ATTRIBS, &m_max_attributes); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, ...) have failed"); m_po_even = PrepareProgram(false); m_po_odd = PrepareProgram(true); PrepareVAO(); PrepareXFB(); is_ok &= TurnOnAttributes(true, false); is_ok &= DrawAndCheck(false); is_ok &= TurnOnAttributes(false, true); is_ok &= DrawAndCheck(true); } catch (...) { is_ok = false; is_error = true; } /* Cleanup. */ Clean(); /* Errors clean up. */ while (gl.getError()) ; /* Result's setup. */ if (is_ok) { m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (is_error) { m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); } else { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } return STOP; } glw::GLuint EnableDisableAttributesTest::PrepareProgram(const bool bind_even_or_odd) { /* Preprocess vertex shader sources. */ std::string declarations = ""; std::string copies = " sum = 0;\n"; for (glw::GLint i = (glw::GLint)(bind_even_or_odd); i < m_max_attributes; i += 2) { declarations.append((std::string("in int a_").append(Utilities::itoa(i))).append(";\n")); copies.append((std::string(" sum += a_").append(Utilities::itoa(i))).append(";\n")); } std::string vs_template(s_vertex_shader_template); std::string vs_source = Utilities::replace(vs_template, "DECLARATION_TEMPLATE", declarations); vs_source = Utilities::replace(vs_source, "COPY_TEMPLATE", copies); /* Build and compile. */ return BuildProgram(vs_source.c_str(), bind_even_or_odd); } /** @brief Build test's GLSL program. * * @note The function may throw if unexpected error has occured. */ glw::GLuint EnableDisableAttributesTest::BuildProgram(const char* vertex_shader, const bool bind_even_or_odd) { /* Shortcut for GL functionality */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); struct Shader { glw::GLchar const* const source; glw::GLenum const type; glw::GLuint id; } shader[] = { { vertex_shader, GL_VERTEX_SHADER, 0 }, { s_fragment_shader, GL_FRAGMENT_SHADER, 0 } }; glw::GLuint const shader_count = DE_LENGTH_OF_ARRAY(shader); glw::GLuint po = 0; try { /* Create program. */ po = gl.createProgram(); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram call failed."); /* Shader compilation. */ for (glw::GLuint i = 0; i < shader_count; ++i) { if (DE_NULL != shader[i].source) { shader[i].id = gl.createShader(shader[i].type); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader call failed."); gl.attachShader(po, shader[i].id); GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader call failed."); gl.shaderSource(shader[i].id, 1, &(shader[i].source), NULL); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource call failed."); gl.compileShader(shader[i].id); GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader call failed."); glw::GLint status = GL_FALSE; gl.getShaderiv(shader[i].id, GL_COMPILE_STATUS, &status); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv call failed."); if (GL_FALSE == status) { glw::GLint log_size = 0; gl.getShaderiv(shader[i].id, GL_INFO_LOG_LENGTH, &log_size); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv call failed."); glw::GLchar* log_text = new glw::GLchar[log_size]; gl.getShaderInfoLog(shader[i].id, log_size, NULL, &log_text[0]); m_context.getTestContext().getLog() << tcu::TestLog::Message << "Shader compilation has failed.\n" << "Shader type: " << glu::getShaderTypeStr(shader[i].type) << "\n" << "Shader compilation error log:\n" << log_text << "\n" << "Shader source code:\n" << shader[i].source << "\n" << tcu::TestLog::EndMessage; delete[] log_text; GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderInfoLog call failed."); throw 0; } } } /* Transform Feedback setup. */ static const glw::GLchar* xfb_varying = "sum"; gl.transformFeedbackVaryings(po, 1, &xfb_varying, GL_INTERLEAVED_ATTRIBS); GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings call failed."); for (glw::GLint i = (glw::GLint)(bind_even_or_odd); i < m_max_attributes; i += 2) { std::string attribute = std::string("a_").append(Utilities::itoa(i)); gl.bindAttribLocation(po, i, attribute.c_str()); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindAttribLocation call failed."); } /* Link. */ gl.linkProgram(po); GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings call failed."); glw::GLint status = GL_FALSE; gl.getProgramiv(po, GL_LINK_STATUS, &status); if (GL_TRUE == status) { for (glw::GLuint i = 0; i < shader_count; ++i) { if (shader[i].id) { gl.detachShader(po, shader[i].id); GLU_EXPECT_NO_ERROR(gl.getError(), "glDetachShader call failed."); } } } else { glw::GLint log_size = 0; gl.getProgramiv(po, GL_INFO_LOG_LENGTH, &log_size); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv call failed."); glw::GLchar* log_text = new glw::GLchar[log_size]; gl.getProgramInfoLog(po, log_size, NULL, &log_text[0]); m_context.getTestContext().getLog() << tcu::TestLog::Message << "Program linkage has failed due to:\n" << log_text << "\n" << tcu::TestLog::EndMessage; delete[] log_text; GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramInfoLog call failed."); throw 0; } } catch (...) { if (po) { gl.deleteProgram(po); po = 0; } } for (glw::GLuint i = 0; i < shader_count; ++i) { if (0 != shader[i].id) { gl.deleteShader(shader[i].id); shader[i].id = 0; } } if (0 == po) { throw 0; } return po; } /** @brief Prepare vertex array object for the test. */ void EnableDisableAttributesTest::PrepareVAO() { /* Shortcut for GL functionality */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* VAO creation. */ gl.genVertexArrays(1, &m_vao); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenVertexArrays call failed."); gl.bindVertexArray(m_vao); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray call failed."); /* Buffer creation. */ gl.genBuffers(1, &m_bo); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers call failed."); gl.bindBuffer(GL_ARRAY_BUFFER, m_bo); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); glw::GLint* reference_data = new glw::GLint[m_max_attributes]; if (DE_NULL == reference_data) { throw 0; } for (glw::GLint i = 0; i < m_max_attributes; ++i) { reference_data[i] = i; } gl.bufferData(GL_ARRAY_BUFFER, sizeof(glw::GLint) * m_max_attributes, reference_data, GL_STATIC_DRAW); delete[] reference_data; GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData call failed."); /* VAO setup. */ for (glw::GLint i = 0; i < m_max_attributes; ++i) { gl.vertexAttribIPointer(i, 1, GL_INT, static_cast<glw::GLsizei>(sizeof(glw::GLint) * m_max_attributes), glu::BufferOffsetAsPointer(i * sizeof(glw::GLint))); GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribIPointer call failed."); } } /** @brief Prepare buffer object for test GLSL program transform feedback results. */ void EnableDisableAttributesTest::PrepareXFB() { /* Shortcut for GL functionality */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Buffer creation. */ gl.genBuffers(1, &m_bo_xfb); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers call failed."); gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_bo_xfb); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); /* Preparing storage. */ gl.bufferStorage(GL_TRANSFORM_FEEDBACK_BUFFER, sizeof(glw::GLint), NULL, GL_MAP_READ_BIT); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferStorage call failed."); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_bo_xfb); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase call failed."); } /** @brief Draw test program, fetch transform feedback results and compare them with expected values. * * @param [in] bind_even_or_odd Even or odd attribute are enabled. * * @return True if expected results are equal to returned by XFB, false otherwise. */ bool EnableDisableAttributesTest::DrawAndCheck(bool bind_even_or_odd) { /* Shortcut for GL functionality */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Setup state. */ gl.useProgram(bind_even_or_odd ? m_po_odd : m_po_even); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram call failed."); gl.bindVertexArray(m_vao); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray call failed."); gl.beginTransformFeedback(GL_POINTS); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback call failed."); /* Draw. */ gl.drawArrays(GL_POINTS, 0, 1); GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays call failed."); /* State reset. */ gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback call failed."); /* Result query. */ glw::GLint* result_ptr = (glw::GLint*)gl.mapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, GL_READ_ONLY); GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBuffer call failed."); glw::GLint result = *result_ptr; gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER); GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer call failed."); /* Check result and return. */ if (bind_even_or_odd) { glw::GLint reference_sum = 0; for (glw::GLint i = 1; i < m_max_attributes; i += 2) { reference_sum += i; } if (reference_sum == result) { return true; } } else { glw::GLint reference_sum = 0; for (glw::GLint i = 0; i < m_max_attributes; i += 2) { reference_sum += i; } if (reference_sum == result) { return true; } } return false; } /** @brief Turn on even or odd attributes (up to m_max_attributes) using EnableVertexArrayAttrib function. * * @param [in] enable_even Turn on even attribute indexes. * @param [in] enable_odd Turn on odd attribute indexes. * * @return True if EnableVertexArrayAttrib does not generate any error, false otherwise. */ bool EnableDisableAttributesTest::TurnOnAttributes(bool enable_even, bool enable_odd) { /* Shortcut for GL functionality */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.bindVertexArray(0); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray call failed."); for (glw::GLint i = 0; i < m_max_attributes; ++i) { bool disable = true; if (i % 2) /* if odd */ { if (enable_odd) { gl.enableVertexArrayAttrib(m_vao, i); if (glw::GLenum error = gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glEnableVertexArrayAttrib generated error " << glu::getErrorStr(error) << " when called with VAO " << m_vao << " and index " << i << "." << tcu::TestLog::EndMessage; return false; } disable = false; } } else { if (enable_even) { gl.enableVertexArrayAttrib(m_vao, i); if (glw::GLenum error = gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glEnableVertexArrayAttrib generated error " << glu::getErrorStr(error) << " when called with VAO " << m_vao << " and index " << i << "." << tcu::TestLog::EndMessage; return false; } disable = false; } } if (disable) { gl.disableVertexArrayAttrib(m_vao, i); if (glw::GLenum error = gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glDisableVertexArrayAttrib generated error " << glu::getErrorStr(error) << " when called with VAO " << m_vao << " and index " << i << "." << tcu::TestLog::EndMessage; return false; } } } gl.bindVertexArray(m_vao); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray call failed."); return true; } /** @brief Clean GL objects. */ void EnableDisableAttributesTest::Clean() { /* Shortcut for GL functionality */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.useProgram(0); if (m_po_even) { gl.deleteProgram(m_po_even); m_po_even = 0; } if (m_po_odd) { gl.deleteProgram(m_po_odd); m_po_odd = 0; } if (m_vao) { gl.deleteVertexArrays(1, &m_vao); m_vao = 0; } if (m_bo) { gl.deleteBuffers(1, &m_bo); m_bo = 0; } if (m_bo_xfb) { gl.deleteBuffers(1, &m_bo_xfb); m_bo_xfb = 0; } if (m_max_attributes) { m_max_attributes = 16; /* OpenGL 4.5 Required Minimum Table 23.57: Implementation Dependent Vertex Shader Limits */ } while (gl.getError()) ; } std::string Utilities::itoa(glw::GLuint i) { std::string s = ""; std::stringstream ss; ss << i; s = ss.str(); return s; } std::string Utilities::replace(const std::string& src, const std::string& key, const std::string& value) { size_t pos = 0; std::string dst = src; while (std::string::npos != (pos = dst.find(key, pos))) { dst.replace(pos, key.length(), value); pos += key.length(); } return dst; } const glw::GLchar EnableDisableAttributesTest::s_vertex_shader_template[] = "#version 450\n" "\n" "DECLARATION_TEMPLATE" "out int sum;\n" "\n" "void main()\n" "{\n" "COPY_TEMPLATE" "}\n"; const glw::GLchar EnableDisableAttributesTest::s_fragment_shader[] = "#version 450\n" "\n" "out vec4 color;\n" "\n" "void main()\n" "{\n" " color = vec4(1.0);" "}\n"; /******************************** Vertex Array Object Element Buffer Test Implementation ********************************/ /** @brief Vertex Array Object Element Buffer Test constructor. * * @param [in] context OpenGL context. */ ElementBufferTest::ElementBufferTest(deqp::Context& context) : deqp::TestCase(context, "vertex_arrays_element_buffer", "Vertex Array Objects Element Buffer Test") , m_po(0) , m_vao(0) , m_bo_array(0) , m_bo_elements(0) , m_bo_xfb(0) { /* Intentionally left blank. */ } /** @brief Iterate Vertex Array Object Enable Disable Attributes Test cases. * * @return Iteration result. */ tcu::TestNode::IterateResult ElementBufferTest::iterate() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Get context setup. */ bool is_at_least_gl_45 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5))); bool is_arb_direct_state_access = m_context.getContextInfo().isExtensionSupported("GL_ARB_direct_state_access"); if ((!is_at_least_gl_45) && (!is_arb_direct_state_access)) { m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not Supported"); return STOP; } /* Running tests. */ bool is_ok = true; bool is_error = false; try { PrepareProgram(); is_ok &= PrepareVAO(); PrepareXFB(); is_ok &= DrawAndCheck(); } catch (...) { is_ok = false; is_error = true; } /* Cleanup. */ Clean(); /* Errors clean up. */ while (gl.getError()) ; /* Result's setup. */ if (is_ok) { m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (is_error) { m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); } else { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } return STOP; } /** @brief Build test's GLSL program. * * @note The function may throw if unexpected error has occured. */ void ElementBufferTest::PrepareProgram() { /* Shortcut for GL functionality */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); struct Shader { glw::GLchar const* const source; glw::GLenum const type; glw::GLuint id; } shader[] = { { s_vertex_shader, GL_VERTEX_SHADER, 0 }, { s_fragment_shader, GL_FRAGMENT_SHADER, 0 } }; glw::GLuint const shader_count = DE_LENGTH_OF_ARRAY(shader); try { /* Create program. */ m_po = gl.createProgram(); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram call failed."); /* Shader compilation. */ for (glw::GLuint i = 0; i < shader_count; ++i) { if (DE_NULL != shader[i].source) { shader[i].id = gl.createShader(shader[i].type); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader call failed."); gl.attachShader(m_po, shader[i].id); GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader call failed."); gl.shaderSource(shader[i].id, 1, &(shader[i].source), NULL); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource call failed."); gl.compileShader(shader[i].id); GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader call failed."); glw::GLint status = GL_FALSE; gl.getShaderiv(shader[i].id, GL_COMPILE_STATUS, &status); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv call failed."); if (GL_FALSE == status) { glw::GLint log_size = 0; gl.getShaderiv(shader[i].id, GL_INFO_LOG_LENGTH, &log_size); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv call failed."); glw::GLchar* log_text = new glw::GLchar[log_size]; gl.getShaderInfoLog(shader[i].id, log_size, NULL, &log_text[0]); m_context.getTestContext().getLog() << tcu::TestLog::Message << "Shader compilation has failed.\n" << "Shader type: " << glu::getShaderTypeStr(shader[i].type) << "\n" << "Shader compilation error log:\n" << log_text << "\n" << "Shader source code:\n" << shader[i].source << "\n" << tcu::TestLog::EndMessage; delete[] log_text; GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderInfoLog call failed."); throw 0; } } } /* Transform Feedback setup. */ static const glw::GLchar* xfb_varying = "result"; gl.transformFeedbackVaryings(m_po, 1, &xfb_varying, GL_INTERLEAVED_ATTRIBS); GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings call failed."); /* Link. */ gl.linkProgram(m_po); GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings call failed."); glw::GLint status = GL_FALSE; gl.getProgramiv(m_po, GL_LINK_STATUS, &status); if (GL_TRUE == status) { for (glw::GLuint i = 0; i < shader_count; ++i) { if (shader[i].id) { gl.detachShader(m_po, shader[i].id); GLU_EXPECT_NO_ERROR(gl.getError(), "glDetachShader call failed."); } } } else { glw::GLint log_size = 0; gl.getProgramiv(m_po, GL_INFO_LOG_LENGTH, &log_size); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv call failed."); glw::GLchar* log_text = new glw::GLchar[log_size]; gl.getProgramInfoLog(m_po, log_size, NULL, &log_text[0]); m_context.getTestContext().getLog() << tcu::TestLog::Message << "Program linkage has failed due to:\n" << log_text << "\n" << tcu::TestLog::EndMessage; delete[] log_text; GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramInfoLog call failed."); throw 0; } } catch (...) { if (m_po) { gl.deleteProgram(m_po); m_po = 0; } } for (glw::GLuint i = 0; i < shader_count; ++i) { if (0 != shader[i].id) { gl.deleteShader(shader[i].id); shader[i].id = 0; } } if (0 == m_po) { throw 0; } } /** @brief Prepare vertex array object for the test of VertexArrayElementBuffer function. * * @return True if function VertexArrayElementBuffer* does not generate any error. */ bool ElementBufferTest::PrepareVAO() { /* Shortcut for GL functionality */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* VAO creation. */ gl.genVertexArrays(1, &m_vao); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenVertexArrays call failed."); gl.bindVertexArray(m_vao); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray call failed."); /* Array buffer creation. */ glw::GLint array_data[3] = { 2, 1, 0 }; gl.genBuffers(1, &m_bo_array); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers call failed."); gl.bindBuffer(GL_ARRAY_BUFFER, m_bo_array); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); gl.bufferData(GL_ARRAY_BUFFER, sizeof(array_data), array_data, GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData call failed."); gl.vertexAttribIPointer(gl.getAttribLocation(m_po, "a"), 1, GL_INT, 0, NULL); GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribIPointer call failed."); gl.enableVertexAttribArray(0); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray call failed."); gl.bindVertexArray(0); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray call failed."); /* Element buffer creation. */ glw::GLuint elements_data[3] = { 2, 1, 0 }; gl.genBuffers(1, &m_bo_elements); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers call failed."); gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_bo_elements); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); gl.bufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements_data), elements_data, GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData call failed."); gl.vertexArrayElementBuffer(m_vao, m_bo_elements); if (glw::GLenum error = gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "VertexArrayElementBuffer has unexpectedly generated " << glu::getErrorStr(error) << "error. Test fails.\n" << tcu::TestLog::EndMessage; return false; } return true; } /** @brief Prepare buffer object for test GLSL program transform feedback results. */ void ElementBufferTest::PrepareXFB() { /* Shortcut for GL functionality */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Buffer creation. */ gl.genBuffers(1, &m_bo_xfb); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers call failed."); gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_bo_xfb); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); /* Preparing storage. */ gl.bufferStorage(GL_TRANSFORM_FEEDBACK_BUFFER, 3 * sizeof(glw::GLint), NULL, GL_MAP_READ_BIT); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferStorage call failed."); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_bo_xfb); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase call failed."); } /** @brief Draw test program, fetch transform feedback results and compare them with expected values. * * @return True if expected results are equal to returned by XFB, false otherwise. */ bool ElementBufferTest::DrawAndCheck() { /* Shortcut for GL functionality */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Setup state. */ gl.useProgram(m_po); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram call failed."); gl.bindVertexArray(m_vao); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray call failed."); gl.beginTransformFeedback(GL_POINTS); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback call failed."); /* Draw. */ gl.drawElements(GL_POINTS, 3, GL_UNSIGNED_INT, NULL); GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays call failed."); /* State reset. */ gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback call failed."); /* Result query. */ glw::GLint* result_ptr = (glw::GLint*)gl.mapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, GL_READ_ONLY); GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBuffer call failed."); glw::GLint result[3] = { result_ptr[0], result_ptr[1], result_ptr[2] }; gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER); GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer call failed."); /* Check result and return. */ for (glw::GLint i = 0; i < 3; ++i) { if (i != result[i]) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Result vector is equal to [" << result[0] << ", " << result[1] << ", " << result[2] << "], but [0, 1, 2] was expected." << tcu::TestLog::EndMessage; return false; } } return true; } /** @brief Clean GL objects. */ void ElementBufferTest::Clean() { /* Shortcut for GL functionality */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.useProgram(0); if (m_po) { gl.deleteProgram(m_po); m_po = 0; } if (m_vao) { gl.deleteVertexArrays(1, &m_vao); m_vao = 0; } if (m_bo_array) { gl.deleteBuffers(1, &m_bo_array); m_bo_array = 0; } if (m_bo_elements) { gl.deleteBuffers(1, &m_bo_elements); m_bo_elements = 0; } if (m_bo_xfb) { gl.deleteBuffers(1, &m_bo_xfb); m_bo_xfb = 0; } while (gl.getError()) ; } const glw::GLchar ElementBufferTest::s_vertex_shader[] = "#version 450\n" "\n" "in int a;" "out int result;\n" "\n" "void main()\n" "{\n" " gl_Position = vec4(1.0);\n" " result = a;" "}\n"; const glw::GLchar ElementBufferTest::s_fragment_shader[] = "#version 450\n" "\n" "out vec4 color;\n" "\n" "void main()\n" "{\n" " color = vec4(1.0);" "}\n"; /******************************** Vertex Array Object Vertex Buffer and Buffers Test Implementation ********************************/ /** @brief Vertex Array Object Element Buffer Test constructor. * * @param [in] context OpenGL context. */ VertexBuffersTest::VertexBuffersTest(deqp::Context& context) : deqp::TestCase(context, "vertex_arrays_vertex_buffers", "Vertex Array Object Vertex Buffer and Buffers Test") , m_po(0) , m_vao(0) , m_bo_array_0(0) , m_bo_array_1(0) , m_bo_xfb(0) { /* Intentionally left blank. */ } /** @brief Iterate Vertex Array Object Enable Disable Attributes Test cases. * * @return Iteration result. */ tcu::TestNode::IterateResult VertexBuffersTest::iterate() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Get context setup. */ bool is_at_least_gl_45 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5))); bool is_arb_direct_state_access = m_context.getContextInfo().isExtensionSupported("GL_ARB_direct_state_access"); if ((!is_at_least_gl_45) && (!is_arb_direct_state_access)) { m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not Supported"); return STOP; } /* Running tests. */ bool is_ok = true; bool is_error = false; try { PrepareProgram(); is_ok &= PrepareVAO(false); PrepareXFB(); is_ok &= DrawAndCheck(); Clean(); PrepareProgram(); is_ok &= PrepareVAO(true); PrepareXFB(); is_ok &= DrawAndCheck(); } catch (...) { is_ok = false; is_error = true; } /* Cleanup. */ Clean(); /* Errors clean up. */ while (gl.getError()) ; /* Result's setup. */ if (is_ok) { m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (is_error) { m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); } else { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } return STOP; } /** @brief Build test's GLSL program. * * @note The function may throw if unexpected error has occured. */ void VertexBuffersTest::PrepareProgram() { /* Shortcut for GL functionality */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); struct Shader { glw::GLchar const* const source; glw::GLenum const type; glw::GLuint id; } shader[] = { { s_vertex_shader, GL_VERTEX_SHADER, 0 }, { s_fragment_shader, GL_FRAGMENT_SHADER, 0 } }; glw::GLuint const shader_count = DE_LENGTH_OF_ARRAY(shader); try { /* Create program. */ m_po = gl.createProgram(); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram call failed."); /* Shader compilation. */ for (glw::GLuint i = 0; i < shader_count; ++i) { if (DE_NULL != shader[i].source) { shader[i].id = gl.createShader(shader[i].type); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader call failed."); gl.attachShader(m_po, shader[i].id); GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader call failed."); gl.shaderSource(shader[i].id, 1, &(shader[i].source), NULL); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource call failed."); gl.compileShader(shader[i].id); GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader call failed."); glw::GLint status = GL_FALSE; gl.getShaderiv(shader[i].id, GL_COMPILE_STATUS, &status); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv call failed."); if (GL_FALSE == status) { glw::GLint log_size = 0; gl.getShaderiv(shader[i].id, GL_INFO_LOG_LENGTH, &log_size); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv call failed."); glw::GLchar* log_text = new glw::GLchar[log_size]; gl.getShaderInfoLog(shader[i].id, log_size, NULL, &log_text[0]); m_context.getTestContext().getLog() << tcu::TestLog::Message << "Shader compilation has failed.\n" << "Shader type: " << glu::getShaderTypeStr(shader[i].type) << "\n" << "Shader compilation error log:\n" << log_text << "\n" << "Shader source code:\n" << shader[i].source << "\n" << tcu::TestLog::EndMessage; delete[] log_text; GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderInfoLog call failed."); throw 0; } } } /* Transform Feedback setup. */ static const glw::GLchar* xfb_varying = "result"; gl.transformFeedbackVaryings(m_po, 1, &xfb_varying, GL_INTERLEAVED_ATTRIBS); GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings call failed."); /* Link. */ gl.linkProgram(m_po); GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings call failed."); glw::GLint status = GL_FALSE; gl.getProgramiv(m_po, GL_LINK_STATUS, &status); if (GL_TRUE == status) { for (glw::GLuint i = 0; i < shader_count; ++i) { if (shader[i].id) { gl.detachShader(m_po, shader[i].id); GLU_EXPECT_NO_ERROR(gl.getError(), "glDetachShader call failed."); } } } else { glw::GLint log_size = 0; gl.getProgramiv(m_po, GL_INFO_LOG_LENGTH, &log_size); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv call failed."); glw::GLchar* log_text = new glw::GLchar[log_size]; gl.getProgramInfoLog(m_po, log_size, NULL, &log_text[0]); m_context.getTestContext().getLog() << tcu::TestLog::Message << "Program linkage has failed due to:\n" << log_text << "\n" << tcu::TestLog::EndMessage; delete[] log_text; GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramInfoLog call failed."); throw 0; } } catch (...) { if (m_po) { gl.deleteProgram(m_po); m_po = 0; } } for (glw::GLuint i = 0; i < shader_count; ++i) { if (0 != shader[i].id) { gl.deleteShader(shader[i].id); shader[i].id = 0; } } if (0 == m_po) { throw 0; } } /** @brief Prepare vertex array object for the test of gl.vertexArrayVertexBuffer* functions. * * @param [in] use_multiple_buffers_function Use gl.vertexArrayVertexBuffers instead of gl.vertexArrayVertexBuffer. * * @return True if functions gl.vertexArrayVertexBuffer* do not generate any error. */ bool VertexBuffersTest::PrepareVAO(bool use_multiple_buffers_function) { /* Shortcut for GL functionality */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* VAO creation. */ gl.genVertexArrays(1, &m_vao); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenVertexArrays call failed."); gl.bindVertexArray(m_vao); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray call failed."); /* Array buffer 0 creation. */ glw::GLint array_data_0[4] = { 0, 2, 1, 3 }; gl.genBuffers(1, &m_bo_array_0); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers call failed."); gl.bindBuffer(GL_ARRAY_BUFFER, m_bo_array_0); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); gl.bufferData(GL_ARRAY_BUFFER, sizeof(array_data_0), array_data_0, GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData call failed."); gl.vertexAttribBinding(gl.getAttribLocation(m_po, "a_0"), 0); gl.vertexAttribBinding(gl.getAttribLocation(m_po, "a_1"), 1); gl.vertexAttribIFormat(gl.getAttribLocation(m_po, "a_0"), 1, GL_INT, 0); gl.vertexAttribIFormat(gl.getAttribLocation(m_po, "a_1"), 1, GL_INT, 0); if (use_multiple_buffers_function) { const glw::GLuint buffers[2] = { m_bo_array_0, m_bo_array_0 }; static const glw::GLintptr offsets[2] = { 0, sizeof(glw::GLint) }; static const glw::GLsizei strides[2] = { sizeof(glw::GLint) * 2, sizeof(glw::GLint) * 2 }; gl.vertexArrayVertexBuffers(m_vao, 0, 2, buffers, offsets, strides); if (glw::GLenum error = gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "VertexArrayVertexBuffers has unexpectedly generated " << glu::getErrorStr(error) << "error. Test fails.\n" << tcu::TestLog::EndMessage; return false; } } else { gl.vertexArrayVertexBuffer(m_vao, 0, m_bo_array_0, (glw::GLintptr)NULL, sizeof(glw::GLint) * 2); if (glw::GLenum error = gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "VertexArrayVertexBuffer has unexpectedly generated " << glu::getErrorStr(error) << "error. Test fails.\n" << tcu::TestLog::EndMessage; return false; } gl.vertexArrayVertexBuffer(m_vao, 1, m_bo_array_0, sizeof(glw::GLint), sizeof(glw::GLint) * 2); if (glw::GLenum error = gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "VertexArrayVertexBuffer has unexpectedly generated " << glu::getErrorStr(error) << "error. Test fails.\n" << tcu::TestLog::EndMessage; return false; } } gl.enableVertexAttribArray(0); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray call failed."); gl.enableVertexAttribArray(1); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray call failed."); /* Array buffer 1 creation. */ glw::GLint array_data_1[2] = { 4, 5 }; gl.genBuffers(1, &m_bo_array_1); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers call failed."); gl.bindBuffer(GL_ARRAY_BUFFER, m_bo_array_1); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); gl.bufferData(GL_ARRAY_BUFFER, sizeof(array_data_1), array_data_1, GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData call failed."); gl.vertexAttribBinding(gl.getAttribLocation(m_po, "a_2"), 2); GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribBinding call failed."); gl.vertexAttribIFormat(gl.getAttribLocation(m_po, "a_2"), 1, GL_INT, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribIFormat call failed."); if (use_multiple_buffers_function) { glw::GLintptr offset = (glw::GLintptr)NULL; glw::GLsizei stride = sizeof(glw::GLint); gl.vertexArrayVertexBuffers(m_vao, 2, 1, &m_bo_array_1, &offset, &stride); if (glw::GLenum error = gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "VertexArrayVertexBuffers has unexpectedly generated " << glu::getErrorStr(error) << "error. Test fails.\n" << tcu::TestLog::EndMessage; return false; } } else { gl.vertexArrayVertexBuffer(m_vao, 2, m_bo_array_1, (glw::GLintptr)NULL, sizeof(glw::GLint)); if (glw::GLenum error = gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "VertexArrayVertexBuffer has unexpectedly generated " << glu::getErrorStr(error) << "error. Test fails.\n" << tcu::TestLog::EndMessage; return false; } } gl.enableVertexAttribArray(2); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray call failed."); gl.bindVertexArray(0); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray call failed."); return true; } /** @brief Prepare buffer object for test GLSL program transform feedback results. */ void VertexBuffersTest::PrepareXFB() { /* Shortcut for GL functionality */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Buffer creation. */ gl.genBuffers(1, &m_bo_xfb); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers call failed."); gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_bo_xfb); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); /* Preparing storage. */ gl.bufferStorage(GL_TRANSFORM_FEEDBACK_BUFFER, 2 * sizeof(glw::GLint), NULL, GL_MAP_READ_BIT); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferStorage call failed."); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_bo_xfb); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase call failed."); } /** @brief Draw test program, fetch transform feedback results and compare them with expected values. * * @return True if expected results are equal to returned by XFB, false otherwise. */ bool VertexBuffersTest::DrawAndCheck() { /* Shortcut for GL functionality */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Setup state. */ gl.useProgram(m_po); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram call failed."); gl.bindVertexArray(m_vao); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray call failed."); gl.beginTransformFeedback(GL_POINTS); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback call failed."); /* Draw. */ gl.drawArrays(GL_POINTS, 0, 2); GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays call failed."); /* State reset. */ gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback call failed."); /* Result query. */ glw::GLint* result_ptr = (glw::GLint*)gl.mapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, GL_READ_ONLY); GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBuffer call failed."); glw::GLint result[2] = { result_ptr[0], result_ptr[1] }; gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER); GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer call failed."); static const glw::GLint reference[2] = { 0 + 2 + 4, 1 + 3 + 5 }; /* Check result and return. */ for (glw::GLint i = 0; i < 2; ++i) { if (reference[i] != result[i]) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Result vector is equal to [" << result[0] << ", " << result[1] << "], but [" << reference[0] << ", " << reference[1] << "] was expected." << tcu::TestLog::EndMessage; return false; } } return true; } /** @brief Clean GL objects. */ void VertexBuffersTest::Clean() { /* Shortcut for GL functionality */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.useProgram(0); if (m_po) { gl.deleteProgram(m_po); m_po = 0; } if (m_vao) { gl.deleteVertexArrays(1, &m_vao); m_vao = 0; } if (m_bo_array_0) { gl.deleteBuffers(1, &m_bo_array_0); m_bo_array_0 = 0; } if (m_bo_array_1) { gl.deleteBuffers(1, &m_bo_array_1); m_bo_array_1 = 0; } if (m_bo_xfb) { gl.deleteBuffers(1, &m_bo_xfb); m_bo_xfb = 0; } while (gl.getError()) ; } const glw::GLchar VertexBuffersTest::s_vertex_shader[] = "#version 450\n" "\n" "in int a_0;" "in int a_1;" "in int a_2;" "\n" "out int result;\n" "\n" "void main()\n" "{\n" " gl_Position = vec4(1.0);\n" " result = a_0 + a_1 + a_2;" "}\n"; const glw::GLchar VertexBuffersTest::s_fragment_shader[] = "#version 450\n" "\n" "out vec4 color;\n" "\n" "void main()\n" "{\n" " color = vec4(1.0);" "}\n"; /******************************** Vertex Array Object Attribute Format Test Implementation ********************************/ /** @brief Vertex Array Object Element Buffer Test constructor. * * @param [in] context OpenGL context. */ AttributeFormatTest::AttributeFormatTest(deqp::Context& context) : deqp::TestCase(context, "vertex_arrays_attribute_format", "Vertex Array Object Attribute Format Test") , m_po(0) , m_vao(0) , m_bo_array(0) , m_bo_xfb(0) { /* Intentionally left blank. */ } /** @brief Iterate Vertex Array Object Enable Disable Attributes Test cases. * * @return Iteration result. */ tcu::TestNode::IterateResult AttributeFormatTest::iterate() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Get context setup. */ bool is_at_least_gl_45 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5))); bool is_arb_direct_state_access = m_context.getContextInfo().isExtensionSupported("GL_ARB_direct_state_access"); if ((!is_at_least_gl_45) && (!is_arb_direct_state_access)) { m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not Supported"); return STOP; } /* Running tests. */ bool is_ok = true; bool is_error = false; try { PrepareXFB(); /* Test floating function. */ for (glw::GLuint i = 1; i <= 4 /* max size */; ++i) { PrepareProgram(i, ATTRIBUTE_FORMAT_FUNCTION_FLOAT); is_ok &= PrepareVAO<glw::GLfloat>(i, GL_FLOAT, false, ATTRIBUTE_FORMAT_FUNCTION_FLOAT); is_ok &= DrawAndCheck<glw::GLfloat>(i, false); CleanVAO(); is_ok &= PrepareVAO<glw::GLbyte>(i, GL_BYTE, false, ATTRIBUTE_FORMAT_FUNCTION_FLOAT); is_ok &= DrawAndCheck<glw::GLfloat>(i, false); CleanVAO(); is_ok &= PrepareVAO<glw::GLbyte>(i, GL_BYTE, true, ATTRIBUTE_FORMAT_FUNCTION_FLOAT); is_ok &= DrawAndCheck<glw::GLfloat>(i, true); CleanVAO(); is_ok &= PrepareVAO<glw::GLubyte>(i, GL_UNSIGNED_BYTE, false, ATTRIBUTE_FORMAT_FUNCTION_FLOAT); is_ok &= DrawAndCheck<glw::GLfloat>(i, false); CleanVAO(); is_ok &= PrepareVAO<glw::GLshort>(i, GL_SHORT, false, ATTRIBUTE_FORMAT_FUNCTION_FLOAT); is_ok &= DrawAndCheck<glw::GLfloat>(i, false); CleanVAO(); is_ok &= PrepareVAO<glw::GLushort>(i, GL_UNSIGNED_SHORT, false, ATTRIBUTE_FORMAT_FUNCTION_FLOAT); is_ok &= DrawAndCheck<glw::GLfloat>(i, false); CleanVAO(); is_ok &= PrepareVAO<glw::GLint>(i, GL_INT, false, ATTRIBUTE_FORMAT_FUNCTION_FLOAT); is_ok &= DrawAndCheck<glw::GLfloat>(i, false); CleanVAO(); is_ok &= PrepareVAO<glw::GLuint>(i, GL_UNSIGNED_INT, false, ATTRIBUTE_FORMAT_FUNCTION_FLOAT); is_ok &= DrawAndCheck<glw::GLfloat>(i, false); CleanVAO(); CleanProgram(); } for (glw::GLuint i = 1; i <= 2 /* max size */; ++i) { PrepareProgram(i, ATTRIBUTE_FORMAT_FUNCTION_DOUBLE); is_ok &= PrepareVAO<glw::GLdouble>(i, GL_DOUBLE, false, ATTRIBUTE_FORMAT_FUNCTION_DOUBLE); is_ok &= DrawAndCheck<glw::GLdouble>(i, false); CleanProgram(); CleanVAO(); } for (glw::GLuint i = 1; i <= 4 /* max size */; ++i) { PrepareProgram(i, ATTRIBUTE_FORMAT_FUNCTION_INTEGER); is_ok &= PrepareVAO<glw::GLbyte>(i, GL_BYTE, false, ATTRIBUTE_FORMAT_FUNCTION_INTEGER); is_ok &= DrawAndCheck<glw::GLint>(i, false); CleanVAO(); is_ok &= PrepareVAO<glw::GLubyte>(i, GL_UNSIGNED_BYTE, false, ATTRIBUTE_FORMAT_FUNCTION_INTEGER); is_ok &= DrawAndCheck<glw::GLint>(i, false); CleanVAO(); is_ok &= PrepareVAO<glw::GLshort>(i, GL_SHORT, false, ATTRIBUTE_FORMAT_FUNCTION_INTEGER); is_ok &= DrawAndCheck<glw::GLint>(i, false); CleanVAO(); is_ok &= PrepareVAO<glw::GLushort>(i, GL_UNSIGNED_SHORT, false, ATTRIBUTE_FORMAT_FUNCTION_INTEGER); is_ok &= DrawAndCheck<glw::GLint>(i, false); CleanVAO(); is_ok &= PrepareVAO<glw::GLint>(i, GL_INT, false, ATTRIBUTE_FORMAT_FUNCTION_INTEGER); is_ok &= DrawAndCheck<glw::GLint>(i, false); CleanVAO(); is_ok &= PrepareVAO<glw::GLuint>(i, GL_UNSIGNED_INT, false, ATTRIBUTE_FORMAT_FUNCTION_INTEGER); is_ok &= DrawAndCheck<glw::GLint>(i, false); CleanVAO(); CleanProgram(); } } catch (...) { is_ok = false; is_error = true; } /* Cleanup. */ CleanProgram(); CleanVAO(); CleanXFB(); /* Errors clean up. */ while (gl.getError()) ; /* Result's setup. */ if (is_ok) { m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (is_error) { m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); } else { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } return STOP; } /** @brief Build test's GLSL program. * * @note The function may throw if unexpected error has occured. */ void AttributeFormatTest::PrepareProgram(glw::GLint size, AtributeFormatFunctionType function_selector) { /* Shortcut for GL functionality */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); struct Shader { glw::GLchar const* source[3]; glw::GLuint const count; glw::GLenum const type; glw::GLuint id; } shader[] = { { { s_vertex_shader_head, s_vertex_shader_declaration[function_selector][size - 1], s_vertex_shader_body }, 3, GL_VERTEX_SHADER, 0 }, { { s_fragment_shader, DE_NULL, DE_NULL }, 1, GL_FRAGMENT_SHADER, 0 } }; glw::GLuint const shader_count = DE_LENGTH_OF_ARRAY(shader); try { /* Create program. */ m_po = gl.createProgram(); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram call failed."); /* Shader compilation. */ for (glw::GLuint i = 0; i < shader_count; ++i) { { shader[i].id = gl.createShader(shader[i].type); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader call failed."); gl.attachShader(m_po, shader[i].id); GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader call failed."); gl.shaderSource(shader[i].id, shader[i].count, shader[i].source, NULL); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource call failed."); gl.compileShader(shader[i].id); GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader call failed."); glw::GLint status = GL_FALSE; gl.getShaderiv(shader[i].id, GL_COMPILE_STATUS, &status); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv call failed."); if (GL_FALSE == status) { glw::GLint log_size = 0; gl.getShaderiv(shader[i].id, GL_INFO_LOG_LENGTH, &log_size); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv call failed."); glw::GLchar* log_text = new glw::GLchar[log_size]; gl.getShaderInfoLog(shader[i].id, log_size, NULL, &log_text[0]); m_context.getTestContext().getLog() << tcu::TestLog::Message << "Shader compilation has failed.\n" << "Shader type: " << glu::getShaderTypeStr(shader[i].type) << "\n" << "Shader compilation error log:\n" << log_text << "\n" << "Shader source code:\n" << shader[i].source << "\n" << tcu::TestLog::EndMessage; delete[] log_text; GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderInfoLog call failed."); throw 0; } } } /* Transform Feedback setup. */ static const glw::GLchar* xfb_varying = "result"; gl.transformFeedbackVaryings(m_po, 1, &xfb_varying, GL_INTERLEAVED_ATTRIBS); GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings call failed."); /* Link. */ gl.linkProgram(m_po); GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings call failed."); glw::GLint status = GL_FALSE; gl.getProgramiv(m_po, GL_LINK_STATUS, &status); if (GL_TRUE == status) { for (glw::GLuint i = 0; i < shader_count; ++i) { if (shader[i].id) { gl.detachShader(m_po, shader[i].id); GLU_EXPECT_NO_ERROR(gl.getError(), "glDetachShader call failed."); } } } else { glw::GLint log_size = 0; gl.getProgramiv(m_po, GL_INFO_LOG_LENGTH, &log_size); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv call failed."); glw::GLchar* log_text = new glw::GLchar[log_size]; gl.getProgramInfoLog(m_po, log_size, NULL, &log_text[0]); m_context.getTestContext().getLog() << tcu::TestLog::Message << "Program linkage has failed due to:\n" << log_text << "\n" << tcu::TestLog::EndMessage; delete[] log_text; GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramInfoLog call failed."); throw 0; } } catch (...) { if (m_po) { gl.deleteProgram(m_po); m_po = 0; } } for (glw::GLuint i = 0; i < shader_count; ++i) { if (0 != shader[i].id) { gl.deleteShader(shader[i].id); shader[i].id = 0; } } if (0 == m_po) { throw 0; } } template <> glw::GLdouble AttributeFormatTest::NormalizationScaleFactor<glw::GLuint>() { return std::pow(2.0, (glw::GLdouble)(CHAR_BIT * sizeof(glw::GLuint) - 4 /* 1.0 / 16.0 */)); } template <> glw::GLdouble AttributeFormatTest::NormalizationScaleFactor<glw::GLushort>() { return std::pow(2.0, (glw::GLdouble)(CHAR_BIT * sizeof(glw::GLushort) - 4 /* 1.0 / 16.0 */)); } template <> glw::GLdouble AttributeFormatTest::NormalizationScaleFactor<glw::GLubyte>() { return std::pow(2.0, (glw::GLdouble)(CHAR_BIT * sizeof(glw::GLubyte) - 4 /* 1.0 / 16.0 */)); } template <> glw::GLdouble AttributeFormatTest::NormalizationScaleFactor<glw::GLint>() { return std::pow(2.0, (glw::GLdouble)(CHAR_BIT * sizeof(glw::GLint) - 4 /* 1.0 / 16.0 */ - 1 /* sign bit */)); } template <> glw::GLdouble AttributeFormatTest::NormalizationScaleFactor<glw::GLshort>() { return std::pow(2.0, (glw::GLdouble)(CHAR_BIT * sizeof(glw::GLshort) - 4 /* 1.0 / 16.0 */ - 1 /* sign bit */)); } template <> glw::GLdouble AttributeFormatTest::NormalizationScaleFactor<glw::GLbyte>() { return std::pow(2.0, (glw::GLdouble)(CHAR_BIT * sizeof(glw::GLbyte) - 4 /* 1.0 / 16.0 */ - 1 /* sign bit */)); } template <typename T> glw::GLdouble AttributeFormatTest::NormalizationScaleFactor() { return 1.0; /* Rest of the types cannot be normalized. */ } /** @brief Prepare vertex array object for the test of VertexArrayAttrib*Format function. * * @param [in] size Size passed to VertexArrayAttrib*Format. * @param [in] type_gl_name Type passed to VertexArrayAttrib*Format. * @param [in] function_selector Selects one of VertexArrayAttrib*Format functions. * * @return True if function VertexArrayAttrib*Format does not generate any error. */ template <typename T> bool AttributeFormatTest::PrepareVAO(glw::GLint size, glw::GLenum type_gl_name, bool normalized, AtributeFormatFunctionType function_selector) { /* Shortcut for GL functionality */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* VAO creation. */ gl.genVertexArrays(1, &m_vao); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenVertexArrays call failed."); gl.bindVertexArray(m_vao); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray call failed."); /* Array buffer 0 creation. */ const glw::GLdouble scale = normalized ? NormalizationScaleFactor<T>() : 1.0; const T array_data[16] = { (T)(0.0 * scale), (T)(1.0 * scale), (T)(2.0 * scale), (T)(3.0 * scale), (T)(4.0 * scale), (T)(5.0 * scale), (T)(6.0 * scale), (T)(7.0 * scale), (T)(8.0 * scale), (T)(9.0 * scale), (T)(10.0 * scale), (T)(11.0 * scale), (T)(12.0 * scale), (T)(13.0 * scale), (T)(14.0 * scale), (T)(15.0 * scale) }; gl.genBuffers(1, &m_bo_array); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers call failed."); gl.bindBuffer(GL_ARRAY_BUFFER, m_bo_array); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); gl.bufferData(GL_ARRAY_BUFFER, sizeof(array_data), array_data, GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData call failed."); /* Attribute setup. */ gl.vertexAttribBinding(gl.getAttribLocation(m_po, "a_0"), 0); GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribBinding call failed."); gl.vertexAttribBinding(gl.getAttribLocation(m_po, "a_1"), 1); GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribBinding call failed."); /* Tested attribute format setup. */ switch (function_selector) { case ATTRIBUTE_FORMAT_FUNCTION_FLOAT: gl.vertexArrayAttribFormat(m_vao, gl.getAttribLocation(m_po, "a_0"), size, type_gl_name, normalized, 0); gl.vertexArrayAttribFormat(m_vao, gl.getAttribLocation(m_po, "a_1"), size, type_gl_name, normalized, 0); break; case ATTRIBUTE_FORMAT_FUNCTION_DOUBLE: gl.vertexArrayAttribLFormat(m_vao, gl.getAttribLocation(m_po, "a_0"), size, type_gl_name, 0); gl.vertexArrayAttribLFormat(m_vao, gl.getAttribLocation(m_po, "a_1"), size, type_gl_name, 0); break; case ATTRIBUTE_FORMAT_FUNCTION_INTEGER: gl.vertexArrayAttribIFormat(m_vao, gl.getAttribLocation(m_po, "a_0"), size, type_gl_name, 0); gl.vertexArrayAttribIFormat(m_vao, gl.getAttribLocation(m_po, "a_1"), size, type_gl_name, 0); break; default: throw 0; } if (glw::GLenum error = gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << ((ATTRIBUTE_FORMAT_FUNCTION_FLOAT == function_selector) ? "VertexArrayAttribFormat" : ((ATTRIBUTE_FORMAT_FUNCTION_DOUBLE == function_selector) ? "VertexArrayAttribLFormat" : ((ATTRIBUTE_FORMAT_FUNCTION_INTEGER == function_selector) ? "VertexArrayAttribIFormat" : "VertexArrayAttrib?Format"))) << " has unexpectedly generated " << glu::getErrorStr(error) << "error for test with size = " << size << ", type = " << glu::getTypeStr(type_gl_name) << ((ATTRIBUTE_FORMAT_FUNCTION_FLOAT == function_selector) ? (normalized ? ", which was normalized." : ", which was not normalized.") : ".") << " Test fails.\n" << tcu::TestLog::EndMessage; return false; } gl.bindVertexBuffer(0, m_bo_array, 0, static_cast<glw::GLsizei>(sizeof(T) * size * 2)); gl.bindVertexBuffer(1, m_bo_array, size * sizeof(T), static_cast<glw::GLsizei>(sizeof(T) * size * 2)); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData call failed."); gl.enableVertexAttribArray(0); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray call failed."); gl.enableVertexAttribArray(1); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray call failed."); gl.bindVertexArray(0); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray call failed."); return true; } /** @brief Prepare buffer object for test GLSL program transform feedback results. */ void AttributeFormatTest::PrepareXFB() { /* Shortcut for GL functionality */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Buffer creation. */ gl.genBuffers(1, &m_bo_xfb); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers call failed."); gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_bo_xfb); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); /* Calculating maximum size. */ glw::GLsizei size = static_cast<glw::GLsizei>( de::max(sizeof(glw::GLubyte), de::max(sizeof(glw::GLbyte), de::max(sizeof(glw::GLushort), de::max(sizeof(glw::GLshort), de::max(sizeof(glw::GLhalf), de::max(sizeof(glw::GLint), de::max(sizeof(glw::GLuint), de::max(sizeof(glw::GLfixed), de::max(sizeof(glw::GLfloat), sizeof(glw::GLdouble)))))))))) * 4 /* maximum number of components */); /* Preparing storage. */ gl.bufferStorage(GL_TRANSFORM_FEEDBACK_BUFFER, size, NULL, GL_MAP_READ_BIT); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferStorage call failed."); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_bo_xfb); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase call failed."); } template <> bool AttributeFormatTest::compare<glw::GLfloat>(glw::GLfloat a, glw::GLfloat b) { if (de::abs(a - b) < 0.03125) { return true; } return false; } template <> bool AttributeFormatTest::compare<glw::GLdouble>(glw::GLdouble a, glw::GLdouble b) { if (de::abs(a - b) < 0.03125) { return true; } return false; } template <typename T> bool AttributeFormatTest::compare(T a, T b) { return (a == b); } /** @brief Draw test program, fetch transform feedback results and compare them with expected values. * * @param [in] size Count of elements of the XFB vector is expected. * @param [in] normalized Normalized values are expected. * * @return True if expected results are equal to returned by XFB, false otherwise. */ template <typename T> bool AttributeFormatTest::DrawAndCheck(glw::GLint size, bool normalized) { /* Shortcut for GL functionality */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Setup state. */ gl.useProgram(m_po); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram call failed."); gl.bindVertexArray(m_vao); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray call failed."); /* Draw. */ gl.beginTransformFeedback(GL_POINTS); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback call failed."); gl.drawArrays(GL_POINTS, 0, 2); GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays call failed."); gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback call failed."); /* Result query. */ T* result_ptr = (T*)gl.mapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, GL_READ_ONLY); GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBuffer call failed."); T result[8] = { 0 }; for (glw::GLint i = 0; i < size * 2 /* two points */; ++i) { result[i] = result_ptr[i]; } gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER); GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer call failed."); const glw::GLdouble scale = normalized ? (1.0 / 16.0) /* Floating point scalling factor. */ : 1.0; const T array_data[16] = { (T)(0.0 * scale), (T)(1.0 * scale), (T)(2.0 * scale), (T)(3.0 * scale), (T)(4.0 * scale), (T)(5.0 * scale), (T)(6.0 * scale), (T)(7.0 * scale), (T)(8.0 * scale), (T)(9.0 * scale), (T)(10.0 * scale), (T)(11.0 * scale), (T)(12.0 * scale), (T)(13.0 * scale), (T)(14.0 * scale), (T)(15.0 * scale) }; T reference[8] = { 0 }; for (glw::GLint i = 0; i < 2 /* two points */; ++i) { for (glw::GLint j = 0; j < size /* size components */; ++j) { reference[i * size + j] = array_data[i * size * 2 + j] + array_data[i * size * 2 + j + size]; } } /* Check result and return. */ for (glw::GLint i = 0; i < size * 2 /* two points */; ++i) { if (!AttributeFormatTest::compare<T>(reference[i], result[i])) { std::string reference_str = "[ "; for (glw::GLint j = 0; j < size * 2 /* two points */; ++j) { std::stringstream ss; ss << reference[j]; reference_str.append(ss.str()); if (j < size * 2 - 1 /* if it is not the last value */) { reference_str.append(", "); } else { reference_str.append(" ]"); } } std::string result_str = "[ "; for (glw::GLint j = 0; j < size * 2 /* two points */; ++j) { std::stringstream ss; ss << result[j]; result_str.append(ss.str()); if (j < size * 2 - 1 /* if it is not the last value */) { result_str.append(", "); } else { result_str.append(" ]"); } } m_context.getTestContext().getLog() << tcu::TestLog::Message << "Result vector is equal to " << result_str.c_str() << ", but " << reference_str.c_str() << " was expected." << tcu::TestLog::EndMessage; return false; } } return true; } /** @brief Clean GLSL program object. */ void AttributeFormatTest::CleanProgram() { /* Shortcut for GL functionality */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.useProgram(0); if (m_po) { gl.deleteProgram(m_po); m_po = 0; } } /** @brief Clean Vertex Array Object and related buffer. */ void AttributeFormatTest::CleanVAO() { /* Shortcut for GL functionality */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); if (m_vao) { gl.deleteVertexArrays(1, &m_vao); m_vao = 0; } if (m_bo_array) { gl.deleteBuffers(1, &m_bo_array); m_bo_array = 0; } } /** @brief Clean GL objects related to transform feedback. */ void AttributeFormatTest::CleanXFB() { /* Shortcut for GL functionality */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); if (m_bo_xfb) { gl.deleteBuffers(1, &m_bo_xfb); m_bo_xfb = 0; } while (gl.getError()) ; } const glw::GLchar* AttributeFormatTest::s_vertex_shader_head = "#version 450\n" "\n"; const glw::GLchar* AttributeFormatTest::s_vertex_shader_body = "\n" "void main()\n" "{\n" " gl_Position = vec4(1.0);\n" " result = a_0 + a_1;" "}\n"; const glw::GLchar* AttributeFormatTest::s_vertex_shader_declaration[ATTRIBUTE_FORMAT_FUNCTION_COUNT] [4 /* sizes count */] = { { "in float a_0;" "in float a_1;" "out float result;\n", "in vec2 a_0;" "in vec2 a_1;" "out vec2 result;\n", "in vec3 a_0;" "in vec3 a_1;" "out vec3 result;\n", "in vec4 a_0;" "in vec4 a_1;" "out vec4 result;\n", }, { "in double a_0;" "in double a_1;" "out double result;\n", "in dvec2 a_0;" "in dvec2 a_1;" "out dvec2 result;\n", "in dvec3 a_0;" "in dvec3 a_1;" "out dvec3 result;\n", "in dvec4 a_0;" "in dvec4 a_1;" "out dvec4 result;\n", }, { "in int a_0;" "in int a_1;" "out int result;\n", "in ivec2 a_0;" "in ivec2 a_1;" "out ivec2 result;\n", "in ivec3 a_0;" "in ivec3 a_1;" "out ivec3 result;\n", "in ivec4 a_0;" "in ivec4 a_1;" "out ivec4 result;\n", } }; const glw::GLchar* AttributeFormatTest::s_fragment_shader = "#version 450\n" "\n" "out vec4 color;\n" "\n" "void main()\n" "{\n" " color = vec4(1.0);" "}\n"; /******************************** Vertex Array Object Attribute Binding Test Implementation ********************************/ /** @brief Attribute Binding Test constructor. * * @param [in] context OpenGL context. */ AttributeBindingTest::AttributeBindingTest(deqp::Context& context) : deqp::TestCase(context, "vertex_arrays_attribute_binding", "Vertex Array Objects Attribute Binding Test") , m_po(0) , m_vao(0) , m_bo_array(0) , m_bo_xfb(0) { /* Intentionally left blank. */ } /** @brief Iterate Attribute Binding Test cases. * * @return Iteration result. */ tcu::TestNode::IterateResult AttributeBindingTest::iterate() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Get context setup. */ bool is_at_least_gl_45 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5))); bool is_arb_direct_state_access = m_context.getContextInfo().isExtensionSupported("GL_ARB_direct_state_access"); if ((!is_at_least_gl_45) && (!is_arb_direct_state_access)) { m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not Supported"); return STOP; } /* Running tests. */ bool is_ok = true; bool is_error = false; try { PrepareProgram(); is_ok &= PrepareVAO(); PrepareXFB(); is_ok &= DrawAndCheck(); } catch (...) { is_ok = false; is_error = true; } /* Cleanup. */ Clean(); /* Errors clean up. */ while (gl.getError()) ; /* Result's setup. */ if (is_ok) { m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (is_error) { m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); } else { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } return STOP; } /** @brief Build test's GLSL program. * * @note The function may throw if unexpected error has occured. */ void AttributeBindingTest::PrepareProgram() { /* Shortcut for GL functionality */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); struct Shader { glw::GLchar const* const source; glw::GLenum const type; glw::GLuint id; } shader[] = { { s_vertex_shader, GL_VERTEX_SHADER, 0 }, { s_fragment_shader, GL_FRAGMENT_SHADER, 0 } }; glw::GLuint const shader_count = DE_LENGTH_OF_ARRAY(shader); try { /* Create program. */ m_po = gl.createProgram(); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram call failed."); /* Shader compilation. */ for (glw::GLuint i = 0; i < shader_count; ++i) { if (DE_NULL != shader[i].source) { shader[i].id = gl.createShader(shader[i].type); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader call failed."); gl.attachShader(m_po, shader[i].id); GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader call failed."); gl.shaderSource(shader[i].id, 1, &(shader[i].source), NULL); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource call failed."); gl.compileShader(shader[i].id); GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader call failed."); glw::GLint status = GL_FALSE; gl.getShaderiv(shader[i].id, GL_COMPILE_STATUS, &status); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv call failed."); if (GL_FALSE == status) { glw::GLint log_size = 0; gl.getShaderiv(shader[i].id, GL_INFO_LOG_LENGTH, &log_size); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv call failed."); glw::GLchar* log_text = new glw::GLchar[log_size]; gl.getShaderInfoLog(shader[i].id, log_size, NULL, &log_text[0]); m_context.getTestContext().getLog() << tcu::TestLog::Message << "Shader compilation has failed.\n" << "Shader type: " << glu::getShaderTypeStr(shader[i].type) << "\n" << "Shader compilation error log:\n" << log_text << "\n" << "Shader source code:\n" << shader[i].source << "\n" << tcu::TestLog::EndMessage; delete[] log_text; GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderInfoLog call failed."); throw 0; } } } /* Binding attributes. */ gl.bindAttribLocation(m_po, 0, "a_0"); gl.bindAttribLocation(m_po, 1, "a_1"); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindAttribLocation call failed."); /* Transform Feedback setup. */ static const glw::GLchar* xfb_varying = "result"; gl.transformFeedbackVaryings(m_po, 1, &xfb_varying, GL_INTERLEAVED_ATTRIBS); GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings call failed."); /* Link. */ gl.linkProgram(m_po); GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings call failed."); glw::GLint status = GL_FALSE; gl.getProgramiv(m_po, GL_LINK_STATUS, &status); if (GL_TRUE == status) { for (glw::GLuint i = 0; i < shader_count; ++i) { if (shader[i].id) { gl.detachShader(m_po, shader[i].id); GLU_EXPECT_NO_ERROR(gl.getError(), "glDetachShader call failed."); } } } else { glw::GLint log_size = 0; gl.getProgramiv(m_po, GL_INFO_LOG_LENGTH, &log_size); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv call failed."); glw::GLchar* log_text = new glw::GLchar[log_size]; gl.getProgramInfoLog(m_po, log_size, NULL, &log_text[0]); m_context.getTestContext().getLog() << tcu::TestLog::Message << "Program linkage has failed due to:\n" << log_text << "\n" << tcu::TestLog::EndMessage; delete[] log_text; GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramInfoLog call failed."); throw 0; } } catch (...) { if (m_po) { gl.deleteProgram(m_po); m_po = 0; } } for (glw::GLuint i = 0; i < shader_count; ++i) { if (0 != shader[i].id) { gl.deleteShader(shader[i].id); shader[i].id = 0; } } if (0 == m_po) { throw 0; } } /** @brief Prepare vertex array object for the test. * * @return True if function VertexArrayAttribBinding does not generate any error. */ bool AttributeBindingTest::PrepareVAO() { /* Shortcut for GL functionality */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* VAO creation. */ gl.genVertexArrays(1, &m_vao); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenVertexArrays call failed."); gl.bindVertexArray(m_vao); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray call failed."); /* Array buffer creation. */ glw::GLint array_data[2] = { 1, 0 }; gl.genBuffers(1, &m_bo_array); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers call failed."); gl.bindBuffer(GL_ARRAY_BUFFER, m_bo_array); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); gl.bufferData(GL_ARRAY_BUFFER, sizeof(array_data), array_data, GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData call failed."); gl.vertexAttribIPointer(0, 1, GL_INT, sizeof(glw::GLint) * 2, NULL); GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribIPointer call failed."); gl.vertexAttribIPointer(1, 1, GL_INT, sizeof(glw::GLint) * 2, glu::BufferOffsetAsPointer(1 * sizeof(glw::GLint))); GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribIPointer call failed."); gl.enableVertexAttribArray(0); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray call failed."); gl.enableVertexAttribArray(1); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray call failed."); gl.vertexArrayAttribBinding(m_vao, 0, 1); gl.vertexArrayAttribBinding(m_vao, 1, 0); if (glw::GLenum error = gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "VertexArrayAttribBinding has unexpectedly generated " << glu::getErrorStr(error) << "error. Test fails.\n" << tcu::TestLog::EndMessage; return false; } return true; } /** @brief Prepare buffer object for test GLSL program transform feedback results. */ void AttributeBindingTest::PrepareXFB() { /* Shortcut for GL functionality */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Buffer creation. */ gl.genBuffers(1, &m_bo_xfb); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers call failed."); gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_bo_xfb); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); /* Preparing storage. */ gl.bufferStorage(GL_TRANSFORM_FEEDBACK_BUFFER, 2 * sizeof(glw::GLint), NULL, GL_MAP_READ_BIT); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferStorage call failed."); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_bo_xfb); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase call failed."); } /** @brief Draw test program, fetch transform feedback results and compare them with expected values. * * @return True if expected results are equal to returned by XFB, false otherwise. */ bool AttributeBindingTest::DrawAndCheck() { /* Shortcut for GL functionality */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Setup state. */ gl.useProgram(m_po); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram call failed."); gl.bindVertexArray(m_vao); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray call failed."); gl.beginTransformFeedback(GL_POINTS); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback call failed."); /* Draw. */ gl.drawArrays(GL_POINTS, 0, 1); GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays call failed."); /* State reset. */ gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback call failed."); /* Result query. */ glw::GLint* result_ptr = (glw::GLint*)gl.mapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, GL_READ_ONLY); GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBuffer call failed."); glw::GLint result[2] = { result_ptr[0], result_ptr[1] }; gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER); GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer call failed."); /* Check result and return. */ if ((0 == result[0]) || (1 == result[1])) { return true; } return false; } /** @brief Clean GL objects. */ void AttributeBindingTest::Clean() { /* Shortcut for GL functionality */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.useProgram(0); if (m_po) { gl.deleteProgram(m_po); m_po = 0; } if (m_vao) { gl.deleteVertexArrays(1, &m_vao); m_vao = 0; } if (m_bo_array) { gl.deleteBuffers(1, &m_bo_array); m_bo_array = 0; } if (m_bo_xfb) { gl.deleteBuffers(1, &m_bo_xfb); m_bo_xfb = 0; } while (gl.getError()) ; } const glw::GLchar AttributeBindingTest::s_vertex_shader[] = "#version 450\n" "\n" "in int a_0;\n" "in int a_1;\n" "out ivec2 result;\n" "\n" "void main()\n" "{\n" " gl_Position = vec4(1.0);\n" " result[0] = a_0;\n" " result[1] = a_1;\n" "}\n"; const glw::GLchar AttributeBindingTest::s_fragment_shader[] = "#version 450\n" "\n" "out vec4 color;\n" "\n" "void main()\n" "{\n" " color = vec4(1.0);" "}\n"; /******************************** Vertex Array Attribute Binding Divisor Test Implementation ********************************/ /** @brief Vertex Array Attribute Binding Divisor Test constructor. * * @param [in] context OpenGL context. */ AttributeBindingDivisorTest::AttributeBindingDivisorTest(deqp::Context& context) : deqp::TestCase(context, "vertex_arrays_attribute_binding_divisor", "Vertex Array Attribute Binding Divisor Test") , m_po(0) , m_vao(0) , m_bo_array(0) , m_bo_xfb(0) { /* Intentionally left blank. */ } /** @brief Iterate Vertex Array Attribute Binding Divisor Test cases. * * @return Iteration result. */ tcu::TestNode::IterateResult AttributeBindingDivisorTest::iterate() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Get context setup. */ bool is_at_least_gl_45 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5))); bool is_arb_direct_state_access = m_context.getContextInfo().isExtensionSupported("GL_ARB_direct_state_access"); if ((!is_at_least_gl_45) && (!is_arb_direct_state_access)) { m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not Supported"); return STOP; } /* Running tests. */ bool is_ok = true; bool is_error = false; try { PrepareProgram(); PrepareVAO(); PrepareXFB(); { glw::GLint reference[] = { 0, 2 }; is_ok = SetDivisor(2); Draw(1, 2); is_ok = CheckXFB((sizeof(reference) / sizeof(reference[0])), reference, "Draw of 1 point with 2 instances with 2 divisor has failed."); } { glw::GLint reference[] = { 0, 0, 1, 1 }; is_ok = SetDivisor(1); Draw(2, 2); is_ok = CheckXFB((sizeof(reference) / sizeof(reference[0])), reference, "Draw of 2 points with 2 instances with 1 divisor has failed."); } { glw::GLint reference[] = { 0, 1, 2, 3 }; is_ok = SetDivisor(1); Draw(1, 4); is_ok = CheckXFB((sizeof(reference) / sizeof(reference[0])), reference, "Draw of 1 point with 4 instances with 1 divisor has failed."); } { glw::GLint reference[] = { 0, 1, 0, 1 }; is_ok = SetDivisor(0); Draw(2, 2); is_ok = CheckXFB((sizeof(reference) / sizeof(reference[0])), reference, "Draw of 2 points with 2 instances with 0 divisor has failed."); } { glw::GLint reference[] = { 0, 1, 2, 3 }; is_ok = SetDivisor(0); Draw(4, 1); is_ok = CheckXFB((sizeof(reference) / sizeof(reference[0])), reference, "Draw of 4 points with 1 instance with 0 divisor has failed."); } } catch (...) { is_ok = false; is_error = true; } /* Cleanup. */ Clean(); /* Errors clean up. */ while (gl.getError()) ; /* Result's setup. */ if (is_ok) { m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (is_error) { m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); } else { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } return STOP; } /** @brief Build test's GLSL program. * * @note The function may throw if unexpected error has occured. */ void AttributeBindingDivisorTest::PrepareProgram() { /* Shortcut for GL functionality */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); struct Shader { glw::GLchar const* const source; glw::GLenum const type; glw::GLuint id; } shader[] = { { s_vertex_shader, GL_VERTEX_SHADER, 0 }, { s_fragment_shader, GL_FRAGMENT_SHADER, 0 } }; glw::GLuint const shader_count = sizeof(shader) / sizeof(shader[0]); try { /* Create program. */ m_po = gl.createProgram(); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram call failed."); /* Shader compilation. */ for (glw::GLuint i = 0; i < shader_count; ++i) { if (DE_NULL != shader[i].source) { shader[i].id = gl.createShader(shader[i].type); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader call failed."); gl.attachShader(m_po, shader[i].id); GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader call failed."); gl.shaderSource(shader[i].id, 1, &(shader[i].source), NULL); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource call failed."); gl.compileShader(shader[i].id); GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader call failed."); glw::GLint status = GL_FALSE; gl.getShaderiv(shader[i].id, GL_COMPILE_STATUS, &status); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv call failed."); if (GL_FALSE == status) { glw::GLint log_size = 0; gl.getShaderiv(shader[i].id, GL_INFO_LOG_LENGTH, &log_size); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv call failed."); glw::GLchar* log_text = new glw::GLchar[log_size]; gl.getShaderInfoLog(shader[i].id, log_size, NULL, &log_text[0]); m_context.getTestContext().getLog() << tcu::TestLog::Message << "Shader compilation has failed.\n" << "Shader type: " << glu::getShaderTypeStr(shader[i].type) << "\n" << "Shader compilation error log:\n" << log_text << "\n" << "Shader source code:\n" << shader[i].source << "\n" << tcu::TestLog::EndMessage; delete[] log_text; GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderInfoLog call failed."); throw 0; } } } /* Transform Feedback setup. */ static const glw::GLchar* xfb_varying = "result"; gl.transformFeedbackVaryings(m_po, 1, &xfb_varying, GL_INTERLEAVED_ATTRIBS); GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings call failed."); /* Link. */ gl.linkProgram(m_po); GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings call failed."); glw::GLint status = GL_FALSE; gl.getProgramiv(m_po, GL_LINK_STATUS, &status); if (GL_TRUE == status) { for (glw::GLuint i = 0; i < shader_count; ++i) { if (shader[i].id) { gl.detachShader(m_po, shader[i].id); GLU_EXPECT_NO_ERROR(gl.getError(), "glDetachShader call failed."); } } } else { glw::GLint log_size = 0; gl.getProgramiv(m_po, GL_INFO_LOG_LENGTH, &log_size); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv call failed."); glw::GLchar* log_text = new glw::GLchar[log_size]; gl.getProgramInfoLog(m_po, log_size, NULL, &log_text[0]); m_context.getTestContext().getLog() << tcu::TestLog::Message << "Program linkage has failed due to:\n" << log_text << "\n" << tcu::TestLog::EndMessage; delete[] log_text; GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramInfoLog call failed."); throw 0; } } catch (...) { if (m_po) { gl.deleteProgram(m_po); m_po = 0; } } for (glw::GLuint i = 0; i < shader_count; ++i) { if (0 != shader[i].id) { gl.deleteShader(shader[i].id); shader[i].id = 0; } } if (m_po) { gl.useProgram(m_po); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram call failed."); } if (0 == m_po) { throw 0; } } /** @brief Prepare vertex array object for the test. */ void AttributeBindingDivisorTest::PrepareVAO() { /* Shortcut for GL functionality */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* VAO creation. */ gl.genVertexArrays(1, &m_vao); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenVertexArrays call failed."); gl.bindVertexArray(m_vao); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray call failed."); /* Array buffer 0 creation. */ glw::GLint array_data[4] = { 0, 1, 2, 3 }; gl.genBuffers(1, &m_bo_array); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers call failed."); gl.bindBuffer(GL_ARRAY_BUFFER, m_bo_array); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); gl.bufferData(GL_ARRAY_BUFFER, sizeof(array_data), array_data, GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData call failed."); gl.vertexAttribBinding(gl.getAttribLocation(m_po, "a"), 0); GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribBinding call failed."); gl.vertexAttribIFormat(gl.getAttribLocation(m_po, "a"), 1, GL_INT, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribIFormat call failed."); gl.bindVertexBuffer(0, m_bo_array, 0, sizeof(glw::GLint)); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexBuffer call failed."); gl.enableVertexAttribArray(gl.getAttribLocation(m_po, "a")); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray call failed."); } /** @brief Prepare buffer object for test GLSL program transform feedback results. */ void AttributeBindingDivisorTest::PrepareXFB() { /* Shortcut for GL functionality */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Buffer creation. */ gl.genBuffers(1, &m_bo_xfb); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers call failed."); gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_bo_xfb); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); /* Preparing storage. */ gl.bufferStorage(GL_TRANSFORM_FEEDBACK_BUFFER, 4 * sizeof(glw::GLint), NULL, GL_MAP_READ_BIT); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferStorage call failed."); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_bo_xfb); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase call failed."); } /** @brief Draw number of points and number of instances with XFB environment. * * @param [in] number_of_points Number of points to be drawn. * @param [in] number_of_instances Number of instances to be drawn. */ void AttributeBindingDivisorTest::Draw(glw::GLuint number_of_points, glw::GLuint number_of_instances) { /* Shortcut for GL functionality */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Setup state. */ gl.beginTransformFeedback(GL_POINTS); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback call failed."); /* Draw. */ gl.drawArraysInstanced(GL_POINTS, 0, number_of_points, number_of_instances); GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArraysInstanced call failed."); /* State reset. */ gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback call failed."); } /** @brief Call VertexArrayBindingDivisor on m_vao object and check errors. * * @param [in] divisor Divisor to be passed. * * @return True if VertexArrayBindingDivisor doe not generate any error, false otherwise. */ bool AttributeBindingDivisorTest::SetDivisor(glw::GLuint divisor) { /* Shortcut for GL functionality */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Setup. */ gl.vertexArrayBindingDivisor(m_vao, 0, divisor); /* Checking for errors (this is tested function so it fail the test if there is error). */ if (glw::GLenum error = gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "VertexArrayBindingDivisor unexpectedl generated " << glu::getErrorStr(error) << " error when called with divisor" << divisor << ". " << tcu::TestLog::EndMessage; return false; } return true; } /** @brief Check transform feedback results and log. * * @param [in] count Number of results to be checked. * @param [in] expected Expected results. * @param [in] log_message Message to be logged if expected values are not equal to queried. * * @return True if expected values are equal to queried, false otherwise. */ bool AttributeBindingDivisorTest::CheckXFB(const glw::GLuint count, const glw::GLint expected[], const glw::GLchar* log_message) { /* Shortcut for GL functionality */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Result setup */ bool is_ok = true; /* Result query. */ glw::GLint* result = (glw::GLint*)gl.mapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, GL_READ_ONLY); GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBuffer call failed."); /* Check result and return. */ for (glw::GLuint i = 0; i < count; ++i) { if (expected[i] != result[i]) { std::string expected_str = "["; std::string result_str = "["; for (glw::GLuint j = 0; j < count; ++j) { expected_str.append(Utilities::itoa((glw::GLuint)expected[j])); result_str.append(Utilities::itoa((glw::GLuint)result[j])); if (j < count - 1) { expected_str.append(", "); result_str.append(", "); } else { expected_str.append("]"); result_str.append("]"); } } m_context.getTestContext().getLog() << tcu::TestLog::Message << "Result is " << result_str << ", but " << expected_str << " was expected. " << log_message << tcu::TestLog::EndMessage; is_ok = false; break; } } /* Unmaping GL buffer. */ gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER); GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer call failed."); return is_ok; } /** @brief Clean GL objects. */ void AttributeBindingDivisorTest::Clean() { /* Shortcut for GL functionality */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.useProgram(0); if (m_po) { gl.deleteProgram(m_po); m_po = 0; } if (m_vao) { gl.deleteVertexArrays(1, &m_vao); m_vao = 0; } if (m_bo_array) { gl.deleteBuffers(1, &m_bo_array); m_bo_array = 0; } if (m_bo_xfb) { gl.deleteBuffers(1, &m_bo_xfb); m_bo_xfb = 0; } while (gl.getError()) ; } const glw::GLchar AttributeBindingDivisorTest::s_vertex_shader[] = "#version 450\n" "\n" "in int a;\n" "out int result;\n" "\n" "void main()\n" "{\n" " gl_Position = vec4(1.0);\n" " result = a;" "}\n"; const glw::GLchar AttributeBindingDivisorTest::s_fragment_shader[] = "#version 450\n" "\n" "out vec4 color;\n" "\n" "void main()\n" "{\n" " color = vec4(1.0);" "}\n"; /******************************** Get Vertex Array Test Implementation ********************************/ /** @brief Get Vertex Array Test constructor. * * @param [in] context OpenGL context. */ GetVertexArrayTest::GetVertexArrayTest(deqp::Context& context) : deqp::TestCase(context, "vertex_arrays_get_vertex_array", "Get Vertex Array Test") { /* Intentionally left blank. */ } /** @brief Iterate Vertex Array Attribute Binding Divisor Test cases. * * @return Iteration result. */ tcu::TestNode::IterateResult GetVertexArrayTest::iterate() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Get context setup. */ bool is_at_least_gl_45 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5))); bool is_arb_direct_state_access = m_context.getContextInfo().isExtensionSupported("GL_ARB_direct_state_access"); if ((!is_at_least_gl_45) && (!is_arb_direct_state_access)) { m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not Supported"); return STOP; } /* Running tests. */ bool is_ok = true; bool is_error = false; /* Test objects. */ glw::GLuint vao = 0; glw::GLuint bo = 0; try { gl.genVertexArrays(1, &vao); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateVertexArrays call failed."); gl.bindVertexArray(vao); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray call failed."); gl.genBuffers(1, &bo); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateBuffers call failed."); gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, bo); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); glw::GLint result = 0; gl.getVertexArrayiv(vao, GL_ELEMENT_ARRAY_BUFFER_BINDING, &result); if (glw::GLenum error = gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GetVertexArrayiv unexpectedly generated " << glu::getErrorStr(error) << "error. Test fails." << tcu::TestLog::EndMessage; is_ok = false; } if ((glw::GLuint)result != bo) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GetVertexArrayiv was expected to return " << bo << ", but " << result << " was observed. Test fails." << tcu::TestLog::EndMessage; is_ok = false; } } catch (...) { is_ok = false; is_error = true; } /* Cleanup. */ if (vao) { gl.deleteVertexArrays(1, &vao); } if (bo) { gl.deleteBuffers(1, &bo); } /* Errors clean up. */ while (gl.getError()) ; /* Result's setup. */ if (is_ok) { m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (is_error) { m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); } else { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } return STOP; } /******************************** Get Vertex Array Test Indexed Implementation ********************************/ /** @brief Get Vertex Array Indexed Test constructor. * * @param [in] context OpenGL context. */ GetVertexArrayIndexedTest::GetVertexArrayIndexedTest(deqp::Context& context) : deqp::TestCase(context, "vertex_arrays_get_vertex_array_indexed", "Get Vertex Array Indexed Test"), m_vao(0) { m_bo[0] = 0; m_bo[1] = 0; m_bo[2] = 0; m_bo[3] = 0; } /** @brief Iterate Vertex Array Attribute Binding Divisor Test cases. * * @return Iteration result. */ tcu::TestNode::IterateResult GetVertexArrayIndexedTest::iterate() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Get context setup. */ bool is_at_least_gl_45 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5))); bool is_arb_direct_state_access = m_context.getContextInfo().isExtensionSupported("GL_ARB_direct_state_access"); if ((!is_at_least_gl_45) && (!is_arb_direct_state_access)) { m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not Supported"); return STOP; } /* Running tests. */ bool is_ok = true; bool is_error = false; try { PrepareVAO(); is_ok &= Check(GL_VERTEX_ATTRIB_ARRAY_ENABLED, 0, GL_TRUE); is_ok &= Check(GL_VERTEX_ATTRIB_ARRAY_ENABLED, 1, GL_TRUE); is_ok &= Check(GL_VERTEX_ATTRIB_ARRAY_ENABLED, 2, GL_TRUE); is_ok &= Check(GL_VERTEX_ATTRIB_ARRAY_ENABLED, 3, GL_TRUE); is_ok &= Check(GL_VERTEX_ATTRIB_ARRAY_ENABLED, 5, GL_FALSE); is_ok &= Check(GL_VERTEX_ATTRIB_ARRAY_STRIDE, 0, 0); is_ok &= Check(GL_VERTEX_ATTRIB_ARRAY_STRIDE, 1, 2); is_ok &= Check(GL_VERTEX_ATTRIB_ARRAY_STRIDE, 2, 0); is_ok &= Check(GL_VERTEX_ATTRIB_ARRAY_STRIDE, 3, 8); is_ok &= Check(GL_VERTEX_ATTRIB_ARRAY_TYPE, 0, GL_BYTE); is_ok &= Check(GL_VERTEX_ATTRIB_ARRAY_TYPE, 1, GL_SHORT); is_ok &= Check(GL_VERTEX_ATTRIB_ARRAY_TYPE, 2, GL_FLOAT); is_ok &= Check(GL_VERTEX_ATTRIB_ARRAY_TYPE, 3, GL_UNSIGNED_INT_2_10_10_10_REV); is_ok &= Check(GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, 0, GL_TRUE); is_ok &= Check(GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, 1, GL_FALSE); is_ok &= Check(GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, 2, GL_FALSE); is_ok &= Check(GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, 3, GL_FALSE); is_ok &= Check(GL_VERTEX_ATTRIB_ARRAY_INTEGER, 0, GL_FALSE); is_ok &= Check(GL_VERTEX_ATTRIB_ARRAY_INTEGER, 1, GL_TRUE); is_ok &= Check(GL_VERTEX_ATTRIB_ARRAY_INTEGER, 2, GL_FALSE); is_ok &= Check(GL_VERTEX_ATTRIB_ARRAY_INTEGER, 3, GL_FALSE); is_ok &= Check(GL_VERTEX_ATTRIB_ARRAY_DIVISOR, 0, 3); is_ok &= Check(GL_VERTEX_ATTRIB_ARRAY_DIVISOR, 1, 2); is_ok &= Check(GL_VERTEX_ATTRIB_ARRAY_DIVISOR, 2, 1); is_ok &= Check(GL_VERTEX_ATTRIB_ARRAY_DIVISOR, 3, 0); is_ok &= Check(GL_VERTEX_ATTRIB_ARRAY_LONG, 0, GL_FALSE); is_ok &= Check(GL_VERTEX_ATTRIB_ARRAY_LONG, 1, GL_FALSE); is_ok &= Check(GL_VERTEX_ATTRIB_ARRAY_LONG, 2, GL_FALSE); is_ok &= Check(GL_VERTEX_ATTRIB_ARRAY_LONG, 3, GL_FALSE); is_ok &= Check(GL_VERTEX_ATTRIB_RELATIVE_OFFSET, 0, 0); is_ok &= Check(GL_VERTEX_ATTRIB_RELATIVE_OFFSET, 1, 0); is_ok &= Check(GL_VERTEX_ATTRIB_RELATIVE_OFFSET, 2, 4); is_ok &= Check(GL_VERTEX_ATTRIB_RELATIVE_OFFSET, 3, 0); is_ok &= Check64(GL_VERTEX_BINDING_OFFSET, 0, 0); is_ok &= Check64(GL_VERTEX_BINDING_OFFSET, 1, 2); is_ok &= Check64(GL_VERTEX_BINDING_OFFSET, 2, 8); is_ok &= Check64(GL_VERTEX_BINDING_OFFSET, 3, 4); } catch (...) { is_ok = false; is_error = true; } /* Cleanup. */ if (m_vao) { gl.deleteVertexArrays(1, &m_vao); m_vao = 0; } if (m_bo[0] || m_bo[1] || m_bo[2] || m_bo[3]) { gl.deleteBuffers(4, m_bo); m_bo[0] = 0; m_bo[1] = 0; m_bo[2] = 0; m_bo[3] = 0; } /* Errors clean up. */ while (gl.getError()) ; /* Result's setup. */ if (is_ok) { m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (is_error) { m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); } else { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } return STOP; } /** @brief Prepare vertex array object for the test. */ void GetVertexArrayIndexedTest::PrepareVAO() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.genVertexArrays(1, &m_vao); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateVertexArrays call failed."); gl.bindVertexArray(m_vao); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray call failed."); gl.genBuffers(4, m_bo); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateBuffers call failed."); /* Attribute 0. */ gl.bindBuffer(GL_ARRAY_BUFFER, m_bo[0]); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); gl.vertexAttribPointer(0, 1, GL_BYTE, GL_TRUE, 0, NULL); GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribPointer call failed."); gl.enableVertexAttribArray(0); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray call failed."); gl.vertexAttribDivisor(0, 3); GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribDivisor call failed."); /* Attribute 1. */ gl.bindBuffer(GL_ARRAY_BUFFER, m_bo[1]); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); gl.vertexAttribIPointer(1, 2, GL_SHORT, 2, glu::BufferOffsetAsPointer(2 * sizeof(glw::GLchar))); GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribPointer call failed."); gl.enableVertexAttribArray(1); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray call failed."); gl.vertexAttribDivisor(1, 2); GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribDivisor call failed."); /* Attribute 2. */ gl.bindBuffer(GL_ARRAY_BUFFER, m_bo[2]); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); gl.vertexAttribBinding(2, 2); GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribBinding call failed."); gl.vertexAttribFormat(2, 3, GL_FLOAT, GL_FALSE, 4); GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribIFormat call failed."); gl.bindVertexBuffer(2, m_bo[2], 8, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexBuffer call failed."); gl.enableVertexAttribArray(2); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray call failed."); gl.vertexAttribDivisor(2, 1); GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribDivisor call failed."); /* Attribute 3. */ gl.bindBuffer(GL_ARRAY_BUFFER, m_bo[3]); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); gl.vertexAttribPointer(3, 4, GL_UNSIGNED_INT_2_10_10_10_REV, GL_FALSE, 8, glu::BufferOffsetAsPointer(4 * sizeof(glw::GLchar))); GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribPointer call failed."); gl.enableVertexAttribArray(3); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray call failed."); gl.vertexAttribDivisor(3, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribDivisor call failed."); } /** @brief Compare value queried using GetVertexArrayIndexediv with expected value and log. * * @param [in] pname Parameter to be queried. * @param [in] index Index to be queried. * @param [in] expected Expected error. * * @return True if value is equal to expected, false otherwise. */ bool GetVertexArrayIndexedTest::Check(const glw::GLenum pname, const glw::GLuint index, const glw::GLint expected) { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); glw::GLint result = 0; gl.getVertexArrayIndexediv(m_vao, index, pname, &result); if (glw::GLenum error = gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GetVertexArrayIndexediv called with index " << index << ", with pname" << glu::getVertexAttribParameterNameStr(pname) << " unexpectedly generated " << glu::getErrorStr(error) << "error. Test fails." << tcu::TestLog::EndMessage; return false; } if (result != expected) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GetVertexArrayIndexediv called with index " << index << " and with pname" << glu::getVertexAttribParameterNameStr(pname) << " returned " << result << ", but " << expected << " was expected. Test fails." << tcu::TestLog::EndMessage; return false; } return true; } /** @brief Compare value queried using GetVertexArrayIndexed64iv with expected value and log. * * @param [in] pname Parameter to be queried. * @param [in] index Index to be queried. * @param [in] expected Expected error. * * @return True if value is equal to expected, false otherwise. */ bool GetVertexArrayIndexedTest::Check64(const glw::GLenum pname, const glw::GLuint index, const glw::GLint64 expected) { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); glw::GLint64 result = 0; gl.getVertexArrayIndexed64iv(m_vao, index, pname, &result); if (glw::GLenum error = gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GetVertexArrayIndexed64iv called with index " << index << ", with pname" << glu::getVertexAttribParameterNameStr(pname) << " unexpectedly generated " << glu::getErrorStr(error) << "error. Test fails." << tcu::TestLog::EndMessage; return false; } if (result != expected) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GetVertexArrayIndexed64iv called with index " << index << " and with pname" << glu::getVertexAttribParameterNameStr(pname) << " returned " << result << ", but " << expected << " was expected. Test fails." << tcu::TestLog::EndMessage; return false; } return true; } /******************************** Defaults Test Implementation ********************************/ /** @brief Defaults Test constructor. * * @param [in] context OpenGL context. */ DefaultsTest::DefaultsTest(deqp::Context& context) : deqp::TestCase(context, "vertex_arrays_defaults", "Defaults Test"), m_vao(0) { } /** @brief Iterate Defaults Test cases. * * @return Iteration result. */ tcu::TestNode::IterateResult DefaultsTest::iterate() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Get context setup. */ bool is_at_least_gl_45 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5))); bool is_arb_direct_state_access = m_context.getContextInfo().isExtensionSupported("GL_ARB_direct_state_access"); if ((!is_at_least_gl_45) && (!is_arb_direct_state_access)) { m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not Supported"); return STOP; } /* Running tests. */ bool is_ok = true; bool is_error = false; /* Test objects. */ glw::GLint max_attributes = 8; try { /* Query limits. */ gl.getIntegerv(GL_MAX_VERTEX_ATTRIBS, &max_attributes); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv call failed."); /* Prepare default Vertex Array Object. */ PrepareVAO(); /* Check default values per attribute index. */ for (glw::GLint i = 0; i < max_attributes; ++i) { is_ok &= CheckIndexed(GL_VERTEX_ATTRIB_ARRAY_ENABLED, i, GL_FALSE); is_ok &= CheckIndexed(GL_VERTEX_ATTRIB_ARRAY_SIZE, i, 4); is_ok &= CheckIndexed(GL_VERTEX_ATTRIB_ARRAY_STRIDE, i, 0); is_ok &= CheckIndexed(GL_VERTEX_ATTRIB_ARRAY_TYPE, i, GL_FLOAT); is_ok &= CheckIndexed(GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, i, GL_FALSE); is_ok &= CheckIndexed(GL_VERTEX_ATTRIB_ARRAY_INTEGER, i, GL_FALSE); is_ok &= CheckIndexed(GL_VERTEX_ATTRIB_ARRAY_DIVISOR, i, 0); is_ok &= CheckIndexed(GL_VERTEX_ATTRIB_ARRAY_LONG, i, GL_FALSE); is_ok &= CheckIndexed(GL_VERTEX_ATTRIB_RELATIVE_OFFSET, i, 0); is_ok &= CheckIndexed64(GL_VERTEX_BINDING_OFFSET, i, 0); } /* Check default values per vertex array object. */ is_ok &= Check(GL_ELEMENT_ARRAY_BUFFER_BINDING, 0); } catch (...) { is_ok = false; is_error = true; } /* Cleanup. */ if (m_vao) { gl.deleteVertexArrays(1, &m_vao); m_vao = 0; } /* Errors clean up. */ while (gl.getError()) ; /* Result's setup. */ if (is_ok) { m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (is_error) { m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); } else { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } return STOP; } /** @brief Prepare vertex array object for the test. */ void DefaultsTest::PrepareVAO() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.createVertexArrays(1, &m_vao); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateVertexArrays call failed."); } /** @brief Compare value queried using GetVertexArrayiv with expected value and log. * * @param [in] pname Parameter to be queried. * @param [in] expected Expected error. * * @return True if value is equal to expected, false otherwise. */ bool DefaultsTest::Check(const glw::GLenum pname, const glw::GLint expected) { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); glw::GLint result = 0; gl.getVertexArrayiv(m_vao, pname, &result); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetVertexArrayiv call failed."); if (result != expected) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Default Vertex Array Object has parameter " << glu::getVertexAttribParameterNameStr(pname) << " equal to " << result << ", but " << expected << " was expected. Test fails." << tcu::TestLog::EndMessage; return false; } return true; } /** @brief Compare value queried using GetVertexArrayIndexediv with expected value and log. * * @param [in] pname Parameter to be queried. * @param [in] index Index to be queried. * @param [in] expected Expected error. * * @return True if value is equal to expected, false otherwise. */ bool DefaultsTest::CheckIndexed(const glw::GLenum pname, const glw::GLuint index, const glw::GLint expected) { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); glw::GLint result = 0; gl.getVertexArrayIndexediv(m_vao, index, pname, &result); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetVertexArrayIndexediv call failed."); if (result != expected) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Default Vertex Array Object at index " << index << " has parameter " << glu::getVertexAttribParameterNameStr(pname) << " equal to " << result << ", but " << expected << " was expected. Test fails." << tcu::TestLog::EndMessage; return false; } return true; } /** @brief Compare value queried using GetVertexArrayIndexed64iv with expected value and log. * * @param [in] pname Parameter to be queried. * @param [in] index Index to be queried. * @param [in] expected Expected error. * * @return True if value is equal to expected, false otherwise. */ bool DefaultsTest::CheckIndexed64(const glw::GLenum pname, const glw::GLuint index, const glw::GLint64 expected) { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); glw::GLint64 result = 0; gl.getVertexArrayIndexed64iv(m_vao, index, pname, &result); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetVertexArrayIndexed64iv call failed."); if (result != expected) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Default Vertex Array Object at index " << index << " has parameter " << glu::getVertexAttribParameterNameStr(pname) << " equal to " << result << ", but " << expected << " was expected. Test fails." << tcu::TestLog::EndMessage; return false; } return true; } /******************************** Creation Error Test Implementation ********************************/ /** @brief Creation Error Test constructor. * * @param [in] context OpenGL context. */ CreationErrorTest::CreationErrorTest(deqp::Context& context) : deqp::TestCase(context, "vertex_arrays_creation_error", "Creation Error Test") { } /** @brief Iterate Creation Error Test cases. * * @return Iteration result. */ tcu::TestNode::IterateResult CreationErrorTest::iterate() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Get context setup. */ bool is_at_least_gl_45 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5))); bool is_arb_direct_state_access = m_context.getContextInfo().isExtensionSupported("GL_ARB_direct_state_access"); if ((!is_at_least_gl_45) && (!is_arb_direct_state_access)) { m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not Supported"); return STOP; } /* Running tests. */ bool is_ok = true; bool is_error = false; try { glw::GLuint negative_vao = 0; gl.createVertexArrays(-1, &negative_vao); is_ok = CheckError(GL_INVALID_VALUE, "INVALID_VALUE was not generated if n is negative."); } catch (...) { is_ok = false; is_error = true; } /* Errors clean up. */ while (gl.getError()) ; /* Result's setup. */ if (is_ok) { m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (is_error) { m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); } else { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } return STOP; } /** @brief Compare error returned by GL with expected value and log. * * @param [in] expected Expected error. * @param [in] log_message Message to be logged if expected error is not the equal to the reported one. * * @return True if GL error is equal to expected, false otherwise. */ bool CreationErrorTest::CheckError(const glw::GLenum expected, const glw::GLchar* log_message) { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); glw::GLenum error = 0; if (expected != (error = gl.getError())) { m_context.getTestContext().getLog() << tcu::TestLog::Message << log_message << " " << glu::getErrorStr(error) << "was observed instead." << tcu::TestLog::EndMessage; return false; } return true; } /******************************** Enable Disable Attribute Errors Test Implementation ********************************/ /** @brief Enable Disable Attribute Errors Test constructor. * * @param [in] context OpenGL context. */ EnableDisableAttributeErrorsTest::EnableDisableAttributeErrorsTest(deqp::Context& context) : deqp::TestCase(context, "vertex_arrays_enable_disable_attribute_errors", "Enable Disable Attribute Errors Test") { } /** @brief Enable Disable Attribute Errors Test cases. * * @return Iteration result. */ tcu::TestNode::IterateResult EnableDisableAttributeErrorsTest::iterate() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Get context setup. */ bool is_at_least_gl_45 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5))); bool is_arb_direct_state_access = m_context.getContextInfo().isExtensionSupported("GL_ARB_direct_state_access"); if ((!is_at_least_gl_45) && (!is_arb_direct_state_access)) { m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not Supported"); return STOP; } /* Running tests. */ bool is_ok = true; bool is_error = false; /* Test objects. */ glw::GLint max_attributes = 8; /* Tested VAOs. */ glw::GLuint vao = 0; glw::GLuint not_a_vao = 0; try { /* Query limits. */ gl.getIntegerv(GL_MAX_VERTEX_ATTRIBS, &max_attributes); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv call failed."); /* Prepare valid VAO. */ gl.createVertexArrays(1, &vao); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateVertexArrays call failed."); /* Prepare invalid VAO. */ while (gl.isVertexArray(++not_a_vao)) ; /* Test not a VAO. */ gl.enableVertexArrayAttrib(0, not_a_vao); is_ok &= CheckError(GL_INVALID_OPERATION, "INVALID_OPERATION was not generated by EnableVertexArrayAttrib if " "vaobj is not the name of an existing vertex array object."); gl.disableVertexArrayAttrib(0, not_a_vao); is_ok &= CheckError(GL_INVALID_OPERATION, "INVALID_OPERATION was not generated by DisableVertexArrayAttrib if " "vaobj is not the name of an existing vertex array object."); /* Test to big attribute index. */ gl.enableVertexArrayAttrib(max_attributes, vao); is_ok &= CheckError( GL_INVALID_OPERATION, "INVALID_VALUE was not generated by EnableVertexArrayAttrib if index is equal to MAX_VERTEX_ATTRIBS."); gl.disableVertexArrayAttrib(max_attributes, vao); is_ok &= CheckError( GL_INVALID_OPERATION, "INVALID_VALUE was not generated by DisableVertexArrayAttrib if index is equal to MAX_VERTEX_ATTRIBS."); gl.enableVertexArrayAttrib(max_attributes + 1, vao); is_ok &= CheckError( GL_INVALID_OPERATION, "INVALID_VALUE was not generated by EnableVertexArrayAttrib if index is greater than MAX_VERTEX_ATTRIBS."); gl.disableVertexArrayAttrib(max_attributes + 1, vao); is_ok &= CheckError( GL_INVALID_OPERATION, "INVALID_VALUE was not generated by DisableVertexArrayAttrib if index is greater than MAX_VERTEX_ATTRIBS."); } catch (...) { is_ok = false; is_error = true; } /* Clean up. */ if (vao) { gl.deleteVertexArrays(1, &vao); } /* Errors clean up. */ while (gl.getError()) ; /* Result's setup. */ if (is_ok) { m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (is_error) { m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); } else { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } return STOP; } /** @brief Compare error returned by GL with expected value and log. * * @param [in] expected Expected error. * @param [in] log_message Message to be logged if expected error is not the equal to the reported one. * * @return True if GL error is equal to expected, false otherwise. */ bool EnableDisableAttributeErrorsTest::CheckError(const glw::GLenum expected, const glw::GLchar* log_message) { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); glw::GLenum error = 0; if (expected != (error = gl.getError())) { m_context.getTestContext().getLog() << tcu::TestLog::Message << log_message << " " << glu::getErrorStr(error) << "was observed instead." << tcu::TestLog::EndMessage; return false; } return true; } /******************************** Element Buffer Errors Test Implementation ********************************/ /** @brief Element Buffer Errors Test constructor. * * @param [in] context OpenGL context. */ ElementBufferErrorsTest::ElementBufferErrorsTest(deqp::Context& context) : deqp::TestCase(context, "vertex_arrays_element_buffer_errors", "Element Buffer Errors Test") { } /** @brief Element Buffer Errors Test cases. * * @return Iteration result. */ tcu::TestNode::IterateResult ElementBufferErrorsTest::iterate() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Get context setup. */ bool is_at_least_gl_45 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5))); bool is_arb_direct_state_access = m_context.getContextInfo().isExtensionSupported("GL_ARB_direct_state_access"); if ((!is_at_least_gl_45) && (!is_arb_direct_state_access)) { m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not Supported"); return STOP; } /* Running tests. */ bool is_ok = true; bool is_error = false; /* Tested Objects. */ glw::GLuint vao = 0; glw::GLuint not_a_vao = 0; glw::GLuint bo = 0; glw::GLuint not_a_bo = 0; try { /* Prepare valid Objects. */ gl.createVertexArrays(1, &vao); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateVertexArrays call failed."); gl.createBuffers(1, &bo); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateBuffers call failed."); /* Prepare invalid VAO. */ while (gl.isVertexArray(++not_a_vao)) ; while (gl.isBuffer(++not_a_bo)) ; /* Test not a VAO. */ gl.vertexArrayElementBuffer(not_a_vao, bo); is_ok &= CheckError(GL_INVALID_OPERATION, "INVALID_OPERATION error was not generated by VertexArrayElementBuffer if " "vaobj is not the name of an existing vertex array object."); /* Test not a BO. */ gl.vertexArrayElementBuffer(vao, not_a_bo); is_ok &= CheckError(GL_INVALID_OPERATION, "INVALID_OPERATION error is generated by VertexArrayElementBuffer if " "buffer is not zero or the name of an existing buffer object."); } catch (...) { is_ok = false; is_error = true; } /* Clean up. */ if (vao) { gl.deleteVertexArrays(1, &vao); } if (bo) { gl.deleteBuffers(1, &bo); } /* Errors clean up. */ while (gl.getError()) ; /* Result's setup. */ if (is_ok) { m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (is_error) { m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); } else { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } return STOP; } /** @brief Compare error returned by GL with expected value and log. * * @param [in] expected Expected error. * @param [in] log_message Message to be logged if expected error is not the equal to the reported one. * * @return True if GL error is equal to expected, false otherwise. */ bool ElementBufferErrorsTest::CheckError(const glw::GLenum expected, const glw::GLchar* log_message) { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); glw::GLenum error = 0; if (expected != (error = gl.getError())) { m_context.getTestContext().getLog() << tcu::TestLog::Message << log_message << " " << glu::getErrorStr(error) << " was observed instead." << tcu::TestLog::EndMessage; return false; } return true; } /******************************** Vertex Buffers Errors Test Implementation ********************************/ /** @brief Vertex Buffers Errors Test constructor. * * @param [in] context OpenGL context. */ VertexBuffersErrorsTest::VertexBuffersErrorsTest(deqp::Context& context) : deqp::TestCase(context, "vertex_arrays_vertex_buffers_errors", "Vertex Buffers Errors Test") { } /** @brief Vertex Buffers Errors Test cases. * * @return Iteration result. */ tcu::TestNode::IterateResult VertexBuffersErrorsTest::iterate() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Get context setup. */ bool is_at_least_gl_45 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5))); bool is_arb_direct_state_access = m_context.getContextInfo().isExtensionSupported("GL_ARB_direct_state_access"); if ((!is_at_least_gl_45) && (!is_arb_direct_state_access)) { m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not Supported"); return STOP; } /* Running tests. */ bool is_ok = true; bool is_error = false; /* Tested Objects. */ glw::GLuint vao = 0; glw::GLuint not_a_vao = 0; glw::GLuint bo = 0; glw::GLuint not_a_bo = 0; /* Valid setup. */ glw::GLintptr valid_offset = 0; glw::GLsizei valid_stride = 1; /* Limits. (Minimum values - OpenGL 4.5 Core Specification, Table 23.55) */ glw::GLint max_vertex_attrib_bindings = 16; glw::GLint max_vertex_attrib_stride = 2048; try { /* Prepare valid Objects. */ gl.createVertexArrays(1, &vao); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateVertexArrays call failed."); gl.createBuffers(1, &bo); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateBuffers call failed."); /* Prepare invalid VAO. */ while (gl.isVertexArray(++not_a_vao)) ; while (gl.isBuffer(++not_a_bo)) ; /* Prepare limits. */ gl.getIntegerv(GL_MAX_VERTEX_ATTRIB_BINDINGS, &max_vertex_attrib_bindings); gl.getIntegerv(GL_MAX_VERTEX_ATTRIB_STRIDE, &max_vertex_attrib_stride); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv call failed."); /* Invalid setup. */ glw::GLintptr invalid_offset = -1; glw::GLsizei invalid_stride_0 = -1; glw::GLsizei invalid_stride_1 = max_vertex_attrib_stride + 1; /* Test not a VAO. */ gl.vertexArrayVertexBuffer(not_a_vao, 0, bo, valid_offset, valid_stride); is_ok &= CheckError(GL_INVALID_OPERATION, "INVALID_OPERATION was not generated by VertexArrayVertexBuffer if " "vaobj is not the name of an existing vertex array object."); gl.vertexArrayVertexBuffers(not_a_vao, 0, 1, &bo, &valid_offset, &valid_stride); is_ok &= CheckError(GL_INVALID_OPERATION, "INVALID_OPERATION was not generated by VertexArrayVertexBuffers if " "vaobj is not the name of an existing vertex array object."); /* Test not a BO. */ gl.vertexArrayVertexBuffer(vao, 0, not_a_bo, valid_offset, valid_stride); is_ok &= CheckError(GL_INVALID_OPERATION, "INVALID_OPERATION was not generated by VertexArrayVertexBuffer if " "vaobj is not the name of an existing vertex array object."); gl.vertexArrayVertexBuffers(vao, 0, 1, ¬_a_bo, &valid_offset, &valid_stride); is_ok &= CheckError(GL_INVALID_OPERATION, "INVALID_OPERATION was not generated by VertexArrayVertexBuffers if " "vaobj is not the name of an existing vertex array object."); /* Test too big binding index. */ gl.vertexArrayVertexBuffer(vao, max_vertex_attrib_bindings, bo, valid_offset, valid_stride); is_ok &= CheckError(GL_INVALID_VALUE, "INVALID_VALUE was not generated by VertexArrayVertexBuffer if " "bindingindex is equal to the value of MAX_VERTEX_ATTRIB_BINDINGS."); gl.vertexArrayVertexBuffer(vao, max_vertex_attrib_bindings + 1, bo, valid_offset, valid_stride); is_ok &= CheckError(GL_INVALID_VALUE, "INVALID_VALUE was not generated by VertexArrayVertexBuffer if " "bindingindex is greater than the value of MAX_VERTEX_ATTRIB_BINDINGS."); gl.vertexArrayVertexBuffers(vao, max_vertex_attrib_bindings, 1, &bo, &valid_offset, &valid_stride); is_ok &= CheckError(GL_INVALID_OPERATION, "INVALID_OPERATION was not generated by VertexArrayVertexBuffers if " "first+count is greater than the value of MAX_VERTEX_ATTRIB_BINDINGS."); /* Test too big stride. */ gl.vertexArrayVertexBuffer(vao, 0, bo, -1, valid_stride); is_ok &= CheckError(GL_INVALID_VALUE, "INVALID_VALUE is generated by VertexArrayVertexBuffer if offset less than zero."); gl.vertexArrayVertexBuffer(vao, 0, bo, valid_offset, -1); is_ok &= CheckError(GL_INVALID_VALUE, "INVALID_VALUE is generated by VertexArrayVertexBuffer if stride is less than zero."); gl.vertexArrayVertexBuffer(vao, 0, bo, valid_offset, max_vertex_attrib_stride + 1); is_ok &= CheckError(GL_INVALID_VALUE, "INVALID_VALUE is generated by VertexArrayVertexBuffer if stride is " "greater than the value of MAX_VERTEX_ATTRIB_STRIDE."); gl.vertexArrayVertexBuffers(vao, 0, 1, &bo, &invalid_offset, &valid_stride); is_ok &= CheckError(GL_INVALID_VALUE, "INVALID_VALUE is generated by VertexArrayVertexBuffers if any value in offsets is negative."); gl.vertexArrayVertexBuffers(vao, 0, 1, &bo, &valid_offset, &invalid_stride_0); is_ok &= CheckError(GL_INVALID_VALUE, "INVALID_VALUE is generated by VertexArrayVertexBuffers if any value in strides is negative."); gl.vertexArrayVertexBuffers(vao, 0, 1, &bo, &valid_offset, &invalid_stride_1); is_ok &= CheckError(GL_INVALID_VALUE, "INVALID_VALUE is generated by VertexArrayVertexBuffers if a value in " "strides is greater than the value of MAX_VERTEX_ATTRIB_STRIDE."); } catch (...) { is_ok = false; is_error = true; } /* Clean up. */ if (vao) { gl.deleteVertexArrays(1, &vao); } if (bo) { gl.deleteBuffers(1, &bo); } /* Errors clean up. */ while (gl.getError()) ; /* Result's setup. */ if (is_ok) { m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (is_error) { m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); } else { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } return STOP; } /** @brief Compare error returned by GL with expected value and log. * * @param [in] expected Expected error. * @param [in] log_message Message to be logged if expected error is not the equal to the reported one. * * @return True if GL error is equal to expected, false otherwise. */ bool VertexBuffersErrorsTest::CheckError(const glw::GLenum expected, const glw::GLchar* log_message) { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); glw::GLenum error = 0; if (expected != (error = gl.getError())) { m_context.getTestContext().getLog() << tcu::TestLog::Message << log_message << " " << glu::getErrorStr(error) << " was observed instead." << tcu::TestLog::EndMessage; return false; } return true; } /******************************** Attribute Format Errors Test Implementation ********************************/ /** @brief Attribute Format Errors Test constructor. * * @param [in] context OpenGL context. */ AttributeFormatErrorsTest::AttributeFormatErrorsTest(deqp::Context& context) : deqp::TestCase(context, "vertex_arrays_attribute_format_errors", "Attribute Format Errors Test") { } /** @brief Attribute Format Errors Test cases. * * @return Iteration result. */ tcu::TestNode::IterateResult AttributeFormatErrorsTest::iterate() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Get context setup. */ bool is_at_least_gl_45 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5))); bool is_arb_direct_state_access = m_context.getContextInfo().isExtensionSupported("GL_ARB_direct_state_access"); if ((!is_at_least_gl_45) && (!is_arb_direct_state_access)) { m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not Supported"); return STOP; } /* Running tests. */ bool is_ok = true; bool is_error = false; /* Tested Objects. */ glw::GLuint vao = 0; glw::GLuint not_a_vao = 0; /* Limits. (Minimum values - OpenGL 4.5 Core Specification, Table 23.55) */ glw::GLint max_vertex_attribs = 16; glw::GLint max_vertex_attrib_relative_offset = 2047; /* Invalid values. */ glw::GLenum bad_type = 0; static const glw::GLenum accepted_types[] = { GL_BYTE, GL_SHORT, GL_INT, GL_FIXED, GL_FLOAT, GL_HALF_FLOAT, GL_DOUBLE, GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, GL_UNSIGNED_INT, GL_INT_2_10_10_10_REV, GL_UNSIGNED_INT_2_10_10_10_REV, GL_UNSIGNED_INT_10F_11F_11F_REV }; { bool is_accepted_type = true; while (is_accepted_type) { bad_type++; is_accepted_type = false; for (glw::GLuint i = 0; i < DE_LENGTH_OF_ARRAY(accepted_types); ++i) { if (accepted_types[i] == bad_type) { is_accepted_type = true; break; } } } } try { /* Prepare valid Objects. */ gl.createVertexArrays(1, &vao); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateVertexArrays call failed."); /* Prepare invalid VAO. */ while (gl.isVertexArray(++not_a_vao)) ; /* Prepare limits. */ gl.getIntegerv(GL_MAX_VERTEX_ATTRIBS, &max_vertex_attribs); gl.getIntegerv(GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET, &max_vertex_attrib_relative_offset); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv call failed."); /* TESTS OF VERTEXARRAYATTRIBFORMAT */ /* MAX_VERTEX_ATTRIBS < */ gl.vertexArrayAttribFormat(vao, max_vertex_attribs, 1, GL_BYTE, GL_FALSE, 0); is_ok &= CheckError(GL_INVALID_VALUE, "INVALID_VALUE was not generated by VertexArrayAttribFormat if " "attribindex is equal to the value of MAX_VERTEX_ATTRIBS."); gl.vertexArrayAttribFormat(vao, max_vertex_attribs + 1, 1, GL_BYTE, GL_FALSE, 0); is_ok &= CheckError(GL_INVALID_VALUE, "INVALID_VALUE was not generated by VertexArrayAttribFormat if " "attribindex is greater than the value of MAX_VERTEX_ATTRIBS."); gl.vertexArrayAttribIFormat(vao, max_vertex_attribs, 1, GL_BYTE, 0); is_ok &= CheckError(GL_INVALID_VALUE, "INVALID_VALUE was not generated by VertexArrayAttribIFormat if " "attribindex is equal to the value of MAX_VERTEX_ATTRIBS."); gl.vertexArrayAttribIFormat(vao, max_vertex_attribs + 1, 1, GL_BYTE, 0); is_ok &= CheckError(GL_INVALID_VALUE, "INVALID_VALUE was not generated by VertexArrayAttribIFormat if " "attribindex is greater than the value of MAX_VERTEX_ATTRIBS."); gl.vertexArrayAttribLFormat(vao, max_vertex_attribs, 1, GL_DOUBLE, 0); is_ok &= CheckError(GL_INVALID_VALUE, "INVALID_VALUE was not generated by VertexArrayAttribLFormat if " "attribindex is equal to the value of MAX_VERTEX_ATTRIBS."); gl.vertexArrayAttribLFormat(vao, max_vertex_attribs + 1, 1, GL_DOUBLE, 0); is_ok &= CheckError(GL_INVALID_VALUE, "INVALID_VALUE was not generated by VertexArrayAttribLFormat if " "attribindex is greater than the value of MAX_VERTEX_ATTRIBS."); /* size */ gl.vertexArrayAttribFormat(vao, 0, 0, GL_BYTE, GL_FALSE, 0); is_ok &= CheckError(GL_INVALID_VALUE, "INVALID_VALUE was not generated by VertexArrayAttrib*Format if size is " "not one of the accepted values (0)."); gl.vertexArrayAttribFormat(vao, 0, 5, GL_BYTE, GL_FALSE, 0); is_ok &= CheckError(GL_INVALID_VALUE, "INVALID_VALUE was not generated by VertexArrayAttrib*Format if size is " "not one of the accepted values (5)."); gl.vertexArrayAttribIFormat(vao, 0, 0, GL_BYTE, 0); is_ok &= CheckError(GL_INVALID_VALUE, "INVALID_VALUE was not generated by VertexArrayAttribIFormat if size is " "not one of the accepted values (0)."); gl.vertexArrayAttribIFormat(vao, 0, 5, GL_BYTE, 0); is_ok &= CheckError(GL_INVALID_VALUE, "INVALID_VALUE was not generated by VertexArrayAttribIFormat if size is " "not one of the accepted values (5)."); gl.vertexArrayAttribLFormat(vao, 0, 0, GL_DOUBLE, 0); is_ok &= CheckError(GL_INVALID_VALUE, "INVALID_VALUE was not generated by VertexArrayAttribLFormat if size is " "not one of the accepted values (0)."); gl.vertexArrayAttribLFormat(vao, 0, 5, GL_DOUBLE, 0); is_ok &= CheckError(GL_INVALID_VALUE, "INVALID_VALUE was not generated by VertexArrayAttribLFormat if size is " "not one of the accepted values (5)."); /* relative offset */ gl.vertexArrayAttribFormat(vao, 0, 1, GL_BYTE, GL_FALSE, max_vertex_attrib_relative_offset + 1); is_ok &= CheckError(GL_INVALID_VALUE, "INVALID_VALUE was not generated by VertexArrayAttrib*Format if " "relativeoffset is greater than the value of " "MAX_VERTEX_ATTRIB_RELATIVE_OFFSET."); gl.vertexArrayAttribIFormat(vao, 0, 1, GL_BYTE, max_vertex_attrib_relative_offset + 1); is_ok &= CheckError(GL_INVALID_VALUE, "INVALID_VALUE was not generated by VertexArrayAttribIFormat if " "relativeoffset is greater than the value of " "MAX_VERTEX_ATTRIB_RELATIVE_OFFSET."); gl.vertexArrayAttribLFormat(vao, 0, 1, GL_DOUBLE, max_vertex_attrib_relative_offset + 1); is_ok &= CheckError(GL_INVALID_VALUE, "INVALID_VALUE was not generated by VertexArrayAttribLFormat if " "relativeoffset is greater than the value of " "MAX_VERTEX_ATTRIB_RELATIVE_OFFSET."); /* type */ gl.vertexArrayAttribFormat(vao, 0, 1, bad_type, GL_FALSE, 0); is_ok &= CheckError( GL_INVALID_ENUM, "INVALID_ENUM was not generated by VertexArrayAttribFormat if type is not one of the accepted tokens."); gl.vertexArrayAttribIFormat(vao, 0, 1, bad_type, 0); is_ok &= CheckError( GL_INVALID_ENUM, "INVALID_ENUM was not generated by VertexArrayAttribIFormat if type is not one of the accepted tokens."); gl.vertexArrayAttribLFormat(vao, 0, 1, bad_type, 0); is_ok &= CheckError( GL_INVALID_ENUM, "INVALID_ENUM was not generated by VertexArrayAttribLFormat if type is not one of the accepted tokens."); /* type UNSIGNED_INT_10F_11F_11F_REV case */ gl.vertexArrayAttribIFormat(vao, 0, 1, GL_UNSIGNED_INT_10F_11F_11F_REV, 0); is_ok &= CheckError( GL_INVALID_ENUM, "INVALID_ENUM was not generated by VertexArrayAttribIFormat if type is UNSIGNED_INT_10F_11F_11F_REV."); gl.vertexArrayAttribLFormat(vao, 0, 1, GL_UNSIGNED_INT_10F_11F_11F_REV, 0); is_ok &= CheckError( GL_INVALID_ENUM, "INVALID_ENUM was not generated by VertexArrayAttribLFormat if type is UNSIGNED_INT_10F_11F_11F_REV."); /* Test not a VAO. */ gl.vertexArrayAttribFormat(not_a_vao, 0, 1, GL_BYTE, GL_FALSE, 0); is_ok &= CheckError(GL_INVALID_OPERATION, "INVALID_OPERATION was not generated by VertexArrayAttribFormat if " "vaobj is not the name of an existing vertex array object."); gl.vertexArrayAttribIFormat(not_a_vao, 0, 1, GL_BYTE, 0); is_ok &= CheckError(GL_INVALID_OPERATION, "INVALID_OPERATION was not generated by VertexArrayAttribIFormat if " "vaobj is not the name of an existing vertex array object."); gl.vertexArrayAttribLFormat(not_a_vao, 0, 1, GL_DOUBLE, 0); is_ok &= CheckError(GL_INVALID_OPERATION, "INVALID_OPERATION was not generated by VertexArrayAttribLFormat if " "vaobj is not the name of an existing vertex array object."); /* BGRA */ gl.vertexArrayAttribFormat(vao, 0, GL_BGRA, GL_BYTE, GL_TRUE, 0); is_ok &= CheckError(GL_INVALID_OPERATION, "INVALID_OPERATION was not generated by VertexArrayAttribFormat if " "size is BGRA and type is not UNSIGNED_BYTE, INT_2_10_10_10_REV or " "UNSIGNED_INT_2_10_10_10_REV."); gl.vertexArrayAttribFormat(vao, 0, 1, GL_INT_2_10_10_10_REV, GL_TRUE, 0); is_ok &= CheckError(GL_INVALID_OPERATION, "INVALID_OPERATION was not generated by VertexArrayAttribFormat if " "type is INT_2_10_10_10_REV and size is neither 4 nor BGRA."); gl.vertexArrayAttribFormat(vao, 0, 1, GL_UNSIGNED_INT_2_10_10_10_REV, GL_TRUE, 0); is_ok &= CheckError(GL_INVALID_OPERATION, "INVALID_OPERATION was not generated by VertexArrayAttribFormat if type " "is UNSIGNED_INT_2_10_10_10_REV and size is neither 4 nor BGRA."); gl.vertexArrayAttribFormat(vao, 0, 1, GL_UNSIGNED_INT_10F_11F_11F_REV, GL_TRUE, 0); is_ok &= CheckError(GL_INVALID_OPERATION, "INVALID_OPERATION was not generated by VertexArrayAttribFormat if " "type is UNSIGNED_INT_10F_11F_11F_REV and size is not 3."); gl.vertexArrayAttribFormat(vao, 0, GL_BGRA, GL_UNSIGNED_BYTE, GL_FALSE, 0); is_ok &= CheckError( GL_INVALID_OPERATION, "INVALID_OPERATION was not generated by VertexArrayAttribFormat if size is BGRA and normalized is FALSE."); } catch (...) { is_ok = false; is_error = true; } /* Clean up. */ if (vao) { gl.deleteVertexArrays(1, &vao); } /* Errors clean up. */ while (gl.getError()) ; /* Result's setup. */ if (is_ok) { m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (is_error) { m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); } else { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } return STOP; } /** @brief Compare error returned by GL with expected value and log. * * @param [in] expected Expected error. * @param [in] log_message Message to be logged if expected error is not the equal to the reported one. * * @return True if GL error is equal to expected, false otherwise. */ bool AttributeFormatErrorsTest::CheckError(const glw::GLenum expected, const glw::GLchar* log_message) { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); glw::GLenum error = 0; if (expected != (error = gl.getError())) { m_context.getTestContext().getLog() << tcu::TestLog::Message << log_message << " " << glu::getErrorStr(error) << " was observed instead." << tcu::TestLog::EndMessage; return false; } return true; } /******************************** Attribute Binding Errors Test Implementation ********************************/ /** @brief Attribute Binding Errors Test constructor. * * @param [in] context OpenGL context. */ AttributeBindingErrorsTest::AttributeBindingErrorsTest(deqp::Context& context) : deqp::TestCase(context, "vertex_arrays_attribute_binding_errors", "Attribute Binding Errors Test") { } /** @brief Attribute Binding Errors Test cases. * * @return Iteration result. */ tcu::TestNode::IterateResult AttributeBindingErrorsTest::iterate() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Get context setup. */ bool is_at_least_gl_45 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5))); bool is_arb_direct_state_access = m_context.getContextInfo().isExtensionSupported("GL_ARB_direct_state_access"); if ((!is_at_least_gl_45) && (!is_arb_direct_state_access)) { m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not Supported"); return STOP; } /* Running tests. */ bool is_ok = true; bool is_error = false; /* Tested Objects. */ glw::GLuint vao = 0; glw::GLuint not_a_vao = 0; /* Limits. (Minimum values - OpenGL 4.5 Core Specification, Table 23.55) */ glw::GLint max_vertex_attribs = 16; glw::GLint max_vertex_attrib_bindings = 16; try { /* Prepare valid Objects. */ gl.createVertexArrays(1, &vao); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateVertexArrays call failed."); /* Prepare invalid VAO. */ while (gl.isVertexArray(++not_a_vao)) ; /* Prepare limits. */ gl.getIntegerv(GL_MAX_VERTEX_ATTRIBS, &max_vertex_attribs); gl.getIntegerv(GL_MAX_VERTEX_ATTRIB_BINDINGS, &max_vertex_attrib_bindings); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv call failed."); /* Not a VAO. */ gl.vertexArrayAttribBinding(not_a_vao, 0, 0); is_ok &= CheckError(GL_INVALID_OPERATION, "INVALID_OPERATION was not generated by VertexArrayAttribBinding if " "vaobj is not the name of an existing vertex array object."); /* Too big attribute index. */ gl.vertexArrayAttribBinding(vao, max_vertex_attribs, 0); is_ok &= CheckError(GL_INVALID_VALUE, "INVALID_VALUE was not generated by VertexArrayAttribBinding if " "attribindex is equal to the value of MAX_VERTEX_ATTRIBS."); gl.vertexArrayAttribBinding(vao, max_vertex_attribs + 1, 0); is_ok &= CheckError(GL_INVALID_VALUE, "INVALID_VALUE was not generated by VertexArrayAttribBinding if " "attribindex is greater than the value of MAX_VERTEX_ATTRIBS."); /* Too big binding index. */ gl.vertexArrayAttribBinding(vao, 0, max_vertex_attrib_bindings); is_ok &= CheckError(GL_INVALID_VALUE, "INVALID_VALUE was not generated by VertexArrayAttribBinding if " "bindingindex is equal to the value of MAX_VERTEX_ATTRIB_BINDINGS."); gl.vertexArrayAttribBinding(vao, 0, max_vertex_attrib_bindings + 1); is_ok &= CheckError(GL_INVALID_VALUE, "INVALID_VALUE was not generated by VertexArrayAttribBinding if " "bindingindex is greater than the value of MAX_VERTEX_ATTRIB_BINDINGS."); } catch (...) { is_ok = false; is_error = true; } /* Clean up. */ if (vao) { gl.deleteVertexArrays(1, &vao); } /* Errors clean up. */ while (gl.getError()) ; /* Result's setup. */ if (is_ok) { m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (is_error) { m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); } else { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } return STOP; } /** @brief Compare error returned by GL with expected value and log. * * @param [in] expected Expected error. * @param [in] log_message Message to be logged if expected error is not the equal to the reported one. * * @return True if GL error is equal to expected, false otherwise. */ bool AttributeBindingErrorsTest::CheckError(const glw::GLenum expected, const glw::GLchar* log_message) { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); glw::GLenum error = 0; if (expected != (error = gl.getError())) { m_context.getTestContext().getLog() << tcu::TestLog::Message << log_message << " " << glu::getErrorStr(error) << " was observed instead." << tcu::TestLog::EndMessage; return false; } return true; } /******************************** Attribute Binding Divisor Errors Test Implementation ********************************/ /** @brief Attribute Binding Divisor Errors Test constructor. * * @param [in] context OpenGL context. */ AttributeBindingDivisorErrorsTest::AttributeBindingDivisorErrorsTest(deqp::Context& context) : deqp::TestCase(context, "vertex_arrays_attribute_binding_divisor_errors", "Attribute Binding Divisor Errors Test") { } /** @brief Attribute Binding Divisor Errors Test cases. * * @return Iteration result. */ tcu::TestNode::IterateResult AttributeBindingDivisorErrorsTest::iterate() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Get context setup. */ bool is_at_least_gl_45 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5))); bool is_arb_direct_state_access = m_context.getContextInfo().isExtensionSupported("GL_ARB_direct_state_access"); if ((!is_at_least_gl_45) && (!is_arb_direct_state_access)) { m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not Supported"); return STOP; } /* Running tests. */ bool is_ok = true; bool is_error = false; /* Tested Objects. */ glw::GLuint vao = 0; glw::GLuint not_a_vao = 0; /* Limits. (Minimum values - OpenGL 4.5 Core Specification, Table 23.55) */ glw::GLint max_vertex_attrib_bindings = 16; try { /* Prepare valid Objects. */ gl.createVertexArrays(1, &vao); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateVertexArrays call failed."); /* Prepare invalid VAO. */ while (gl.isVertexArray(++not_a_vao)) ; /* Prepare limits. */ gl.getIntegerv(GL_MAX_VERTEX_ATTRIB_BINDINGS, &max_vertex_attrib_bindings); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv call failed."); /* Not a VAO. */ gl.vertexArrayBindingDivisor(not_a_vao, 0, 0); is_ok &= CheckError(GL_INVALID_OPERATION, "INVALID_OPERATION was not generated by VertexArrayBindingDivisor if " "vaobj is not the name of an existing vertex array object."); /* Too big binding index. */ gl.vertexArrayBindingDivisor(vao, max_vertex_attrib_bindings, 0); is_ok &= CheckError(GL_INVALID_VALUE, "INVALID_VALUE was not generated by VertexArrayBindingDivisor if " "bindingindex is equal to the value of MAX_VERTEX_ATTRIB_BINDINGS."); gl.vertexArrayBindingDivisor(vao, max_vertex_attrib_bindings + 1, 0); is_ok &= CheckError(GL_INVALID_VALUE, "INVALID_VALUE was not generated by VertexArrayBindingDivisor if " "bindingindex is greater than the value of MAX_VERTEX_ATTRIB_BINDINGS."); } catch (...) { is_ok = false; is_error = true; } /* Clean up. */ if (vao) { gl.deleteVertexArrays(1, &vao); } /* Errors clean up. */ while (gl.getError()) ; /* Result's setup. */ if (is_ok) { m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (is_error) { m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); } else { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } return STOP; } /** @brief Compare error returned by GL with expected value and log. * * @param [in] expected Expected error. * @param [in] log_message Message to be logged if expected error is not the equal to the reported one. * * @return True if GL error is equal to expected, false otherwise. */ bool AttributeBindingDivisorErrorsTest::CheckError(const glw::GLenum expected, const glw::GLchar* log_message) { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); glw::GLenum error = 0; if (expected != (error = gl.getError())) { m_context.getTestContext().getLog() << tcu::TestLog::Message << log_message << " " << glu::getErrorStr(error) << " was observed instead." << tcu::TestLog::EndMessage; return false; } return true; } /******************************** Get Vertex Array Errors Test Implementation ********************************/ /** @brief Get Vertex Array Errors Test constructor. * * @param [in] context OpenGL context. */ GetVertexArrayErrorsTest::GetVertexArrayErrorsTest(deqp::Context& context) : deqp::TestCase(context, "vertex_arrays_get_vertex_array_errors", "Get Vertex Array Errors Test") { } /** @brief Iterate over Get Vertex Array Errors Test cases. * * @return Iteration result. */ tcu::TestNode::IterateResult GetVertexArrayErrorsTest::iterate() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Get context setup. */ bool is_at_least_gl_45 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5))); bool is_arb_direct_state_access = m_context.getContextInfo().isExtensionSupported("GL_ARB_direct_state_access"); if ((!is_at_least_gl_45) && (!is_arb_direct_state_access)) { m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not Supported"); return STOP; } /* Running tests. */ bool is_ok = true; bool is_error = false; /* Tested Objects. */ glw::GLuint vao = 0; glw::GLuint not_a_vao = 0; glw::GLint storage = 0; try { /* Prepare valid Objects. */ gl.createVertexArrays(1, &vao); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateVertexArrays call failed."); /* Prepare invalid VAO. */ while (gl.isVertexArray(++not_a_vao)) ; /* Not a VAO. */ gl.getVertexArrayiv(not_a_vao, GL_ELEMENT_ARRAY_BUFFER_BINDING, &storage); is_ok &= CheckError(GL_INVALID_OPERATION, "INVALID_OPERATION error was not generated by GetVertexArrayiv if " "vaobj is not the name of an existing vertex array object."); /* Bad parameter. */ gl.getVertexArrayiv(vao, GL_ELEMENT_ARRAY_BUFFER_BINDING + 1, &storage); is_ok &= CheckError( GL_INVALID_ENUM, "INVALID_ENUM error was not generated by GetVertexArrayiv if pname is not ELEMENT_ARRAY_BUFFER_BINDING."); } catch (...) { is_ok = false; is_error = true; } /* Clean up. */ if (vao) { gl.deleteVertexArrays(1, &vao); } /* Errors clean up. */ while (gl.getError()) ; /* Result's setup. */ if (is_ok) { m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (is_error) { m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); } else { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } return STOP; } /** @brief Compare error returned by GL with expected value and log. * * @param [in] expected Expected error. * @param [in] log_message Message to be logged if expected error is not the equal to the reported one. * * @return True if GL error is equal to expected, false otherwise. */ bool GetVertexArrayErrorsTest::CheckError(const glw::GLenum expected, const glw::GLchar* log_message) { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); glw::GLenum error = 0; if (expected != (error = gl.getError())) { m_context.getTestContext().getLog() << tcu::TestLog::Message << log_message << " " << glu::getErrorStr(error) << " was observed instead." << tcu::TestLog::EndMessage; return false; } return true; } /******************************** Get Vertex Array Indexed Errors Test Implementation ********************************/ /** @brief Get Vertex Array Indexed Errors Test constructor. * * @param [in] context OpenGL context. */ GetVertexArrayIndexedErrorsTest::GetVertexArrayIndexedErrorsTest(deqp::Context& context) : deqp::TestCase(context, "vertex_arrays_get_vertex_array_indexed_errors", "Get Vertex Array Indexed Errors Test") { } /** @brief Iterate over Get Vertex Array Indexed Errors Test cases. * * @return Iteration result. */ tcu::TestNode::IterateResult GetVertexArrayIndexedErrorsTest::iterate() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Get context setup. */ bool is_at_least_gl_45 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5))); bool is_arb_direct_state_access = m_context.getContextInfo().isExtensionSupported("GL_ARB_direct_state_access"); if ((!is_at_least_gl_45) && (!is_arb_direct_state_access)) { m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not Supported"); return STOP; } /* Running tests. */ bool is_ok = true; bool is_error = false; /* Tested Objects. */ glw::GLuint vao = 0; glw::GLuint not_a_vao = 0; /* Dummy storage. */ glw::GLint storage = 0; glw::GLint64 storage64 = 0; /* Bad parameter setup. */ glw::GLenum bad_pname = 0; static const glw::GLenum accepted_pnames[] = { GL_VERTEX_ATTRIB_ARRAY_ENABLED, GL_VERTEX_ATTRIB_ARRAY_SIZE, GL_VERTEX_ATTRIB_ARRAY_STRIDE, GL_VERTEX_ATTRIB_ARRAY_TYPE, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, GL_VERTEX_ATTRIB_ARRAY_INTEGER, GL_VERTEX_ATTRIB_ARRAY_LONG, GL_VERTEX_ATTRIB_ARRAY_DIVISOR, GL_VERTEX_ATTRIB_RELATIVE_OFFSET }; { bool is_accepted_pname = true; while (is_accepted_pname) { bad_pname++; is_accepted_pname = false; for (glw::GLuint i = 0; i < DE_LENGTH_OF_ARRAY(accepted_pnames); ++i) { if (accepted_pnames[i] == bad_pname) { is_accepted_pname = true; break; } } } } try { /* Prepare valid Objects. */ gl.createVertexArrays(1, &vao); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateVertexArrays call failed."); /* Prepare invalid VAO. */ while (gl.isVertexArray(++not_a_vao)) ; /* Not a VAO. */ gl.getVertexArrayIndexediv(not_a_vao, 0, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &storage); is_ok &= CheckError(GL_INVALID_OPERATION, "INVALID_OPERATION error was not generated by GetVertexArrayIndexediv if " "vaobj is not the name of an existing vertex array object."); gl.getVertexArrayIndexed64iv(not_a_vao, 0, GL_VERTEX_BINDING_OFFSET, &storage64); is_ok &= CheckError(GL_INVALID_OPERATION, "INVALID_OPERATION error was not generated by GetVertexArrayIndexed64iv " "if vaobj is not the name of an existing vertex array object."); /* Bad parameter. */ gl.getVertexArrayIndexediv(vao, 0, bad_pname, &storage); is_ok &= CheckError( GL_INVALID_ENUM, "INVALID_ENUM error was not generated by GetVertexArrayIndexediv if pname is not one of the valid values."); /* Bad parameter 64. */ gl.getVertexArrayIndexed64iv(vao, 0, GL_VERTEX_BINDING_OFFSET + 1, &storage64); is_ok &= CheckError( GL_INVALID_ENUM, "INVALID_ENUM error was not generated by GetVertexArrayIndexed64iv if pname is not VERTEX_BINDING_OFFSET."); } catch (...) { is_ok = false; is_error = true; } /* Clean up. */ if (vao) { gl.deleteVertexArrays(1, &vao); } /* Errors clean up. */ while (gl.getError()) ; /* Result's setup. */ if (is_ok) { m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (is_error) { m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); } else { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } return STOP; } /** @brief Compare error returned by GL with expected value and log. * * @param [in] expected Expected error. * @param [in] log_message Message to be logged if expected error is not the equal to the reported one. * * @return True if GL error is equal to expected, false otherwise. */ bool GetVertexArrayIndexedErrorsTest::CheckError(const glw::GLenum expected, const glw::GLchar* log_message) { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); glw::GLenum error = 0; if (expected != (error = gl.getError())) { m_context.getTestContext().getLog() << tcu::TestLog::Message << log_message << " " << glu::getErrorStr(error) << " was observed instead." << tcu::TestLog::EndMessage; return false; } return true; } } /* VertexArrays namespace. */ } /* DirectStateAccess namespace. */ } /* gl4cts namespace. */