| /*------------------------------------------------------------------------- |
| * 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 = sizeof(shader) / sizeof(shader[0]); |
| |
| 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 = 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 (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 = 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 (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 = 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) |
| { |
| { |
| 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 = 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; |
| } |
| } |
| } |
| |
| /* 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); |
|