/*------------------------------------------------------------------------- * OpenGL Conformance Test Suite * ----------------------------- * * Copyright (c) 2014-2016 The Khronos Group Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ /*! * \file * \brief */ /*-------------------------------------------------------------------*/ /** * @file gl4cVertexAttrib64BitTests.hpp * @brief Implement conformance tests for GL_ARB_vertex_attrib_64bit functionality **/ #include "gl4cVertexAttrib64BitTest.hpp" #include "gluContextInfo.hpp" #include "gluStrUtil.hpp" #include "glwDefs.hpp" #include "glwEnums.hpp" #include "glwFunctions.hpp" #include "tcuTestLog.hpp" #include #include #include #include using namespace glw; namespace VertexAttrib64Bit { class Base : public deqp::TestCase { public: /* Public constructor and destructor */ Base(deqp::Context& context, const char* name, const char* description); virtual ~Base() { } /* Public methods */ void BuildProgram(const GLchar* fragment_shader_code, GLuint& program_id, const GLchar* vertex_shader_code, GLuint& out_fragment_shader_id, GLuint& out_vertex_shader_id) const; void BuildProgramVSOnly(GLuint out_program_id, const GLchar* vertex_shader_code, GLuint& out_vertex_shader_id) const; void CompileShader(GLuint id, const GLchar* source_code) const; GLint GetMaxVertexAttribs() const; void IterateStart(); tcu::TestNode::IterateResult IterateStop(bool result) const; void LinkProgram(GLuint id) const; static GLdouble RandomDouble(GLdouble min, GLdouble max); void RequireExtension(const GLchar* extension_name) const; /* Public fields */ /* Test framework objects */ glw::Functions gl; /* prefix "m_" ommitted for readability */ tcu::TestLog& m_log; }; /** Constructor * **/ Base::Base(deqp::Context& context, const char* name, const char* description) : TestCase(context, name, description), m_log(m_context.getTestContext().getLog()) { /* Nothing to be done here */ } void Base::BuildProgram(const GLchar* fragment_shader_code, GLuint& program_id, const GLchar* vertex_shader_code, GLuint& out_fragment_shader_id, GLuint& out_vertex_shader_id) const { out_fragment_shader_id = gl.createShader(GL_FRAGMENT_SHADER); GLU_EXPECT_NO_ERROR(gl.getError(), "CreateShader"); out_vertex_shader_id = gl.createShader(GL_VERTEX_SHADER); GLU_EXPECT_NO_ERROR(gl.getError(), "CreateShader"); CompileShader(out_fragment_shader_id, fragment_shader_code); CompileShader(out_vertex_shader_id, vertex_shader_code); gl.attachShader(program_id, out_fragment_shader_id); GLU_EXPECT_NO_ERROR(gl.getError(), "AttachShader"); gl.attachShader(program_id, out_vertex_shader_id); GLU_EXPECT_NO_ERROR(gl.getError(), "AttachShader"); LinkProgram(program_id); } /** Builds and links a program object consisting only of vertex shader stage. * The function also creates a vertex shader, assigns it user-provided body * and compiles it. * * @param program_id ID of a created program object to configure. * @param vertex_shader_code Source code to use for the vertex shader. * @param out_vertex_shader_id Will hold **/ void Base::BuildProgramVSOnly(GLuint program_id, const GLchar* vertex_shader_code, GLuint& out_vertex_shader_id) const { out_vertex_shader_id = gl.createShader(GL_VERTEX_SHADER); GLU_EXPECT_NO_ERROR(gl.getError(), "CreateShader"); CompileShader(out_vertex_shader_id, vertex_shader_code); gl.attachShader(program_id, out_vertex_shader_id); GLU_EXPECT_NO_ERROR(gl.getError(), "AttachShader"); LinkProgram(program_id); } void Base::CompileShader(GLuint id, const GLchar* source_code) const { GLint status = 0; gl.shaderSource(id, 1, &source_code, 0 /* length */); GLU_EXPECT_NO_ERROR(gl.getError(), "ShaderSource"); gl.compileShader(id); GLU_EXPECT_NO_ERROR(gl.getError(), "CompileShader"); gl.getShaderiv(id, GL_COMPILE_STATUS, &status); GLU_EXPECT_NO_ERROR(gl.getError(), "GetShaderiv"); if (GL_FALSE == status) { GLint message_length = 0; std::vector message; gl.getShaderiv(id, GL_INFO_LOG_LENGTH, &message_length); GLU_EXPECT_NO_ERROR(gl.getError(), "GetShaderiv"); message.resize(message_length + 1); gl.getShaderInfoLog(id, message_length, &message_length, &message[0]); GLU_EXPECT_NO_ERROR(gl.getError(), "GetShaderInfoLog"); m_log << tcu::TestLog::Section("Shader compilation error", ""); m_log << tcu::TestLog::Message << "Compilation log:\n" << &message[0] << tcu::TestLog::EndMessage; m_log << tcu::TestLog::Message << "Shader source:\n" << source_code << tcu::TestLog::EndMessage; m_log << tcu::TestLog::EndSection; TCU_FAIL("Shader compilation failed"); } } /** Get value of GL_MAX_VERTEX_ATTRIBS * * Throws exception in case of failure * * @return Value **/ GLint Base::GetMaxVertexAttribs() const { GLint max_vertex_attribs; gl.getIntegerv(GL_MAX_VERTEX_ATTRIBS, &max_vertex_attribs); GLU_EXPECT_NO_ERROR(gl.getError(), "GetIntegerv"); return max_vertex_attribs; } void Base::IterateStart() { gl = m_context.getRenderContext().getFunctions(); } tcu::TestNode::IterateResult Base::IterateStop(bool result) const { /* Set test result */ if (false == result) { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } else { m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } /* Done */ return tcu::TestNode::STOP; } void Base::LinkProgram(GLuint id) const { GLint status = 0; gl.linkProgram(id); GLU_EXPECT_NO_ERROR(gl.getError(), "LinkProgram"); gl.getProgramiv(id, GL_LINK_STATUS, &status); GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramiv"); if (GL_FALSE == status) { GLint message_length = 0; std::vector message; gl.getProgramiv(id, GL_INFO_LOG_LENGTH, &message_length); GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramiv"); message.resize(message_length + 1); gl.getProgramInfoLog(id, message_length, &message_length, &message[0]); GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramInfoLog"); m_log << tcu::TestLog::Section("Program link error", ""); m_log << tcu::TestLog::Message << "Link log:\n" << &message[0] << tcu::TestLog::EndMessage; m_log << tcu::TestLog::EndSection; TCU_FAIL("Program linking failed"); } } /** Return "random" double value from range * * @return Value **/ GLdouble Base::RandomDouble(GLdouble min, GLdouble max) { static const glw::GLushort max_value = 0x2000; static glw::GLushort value = 0x1234; GLdouble fraction = ((GLdouble)value) / ((GLdouble)max_value); const GLdouble range = max - min; value = static_cast((max_value <= value) ? 0 : value + 1); return min + fraction * range; } /** Throws tcu::NotSupportedError if requested extensions is not available. * **/ void Base::RequireExtension(const GLchar* extension_name) const { const std::vector& extensions = m_context.getContextInfo().getExtensions(); if (std::find(extensions.begin(), extensions.end(), extension_name) == extensions.end()) { std::string message = "Required extension is not supported: "; message.append(extension_name); throw tcu::NotSupportedError(message); } } /** Implementation of conformance test "1", description follows. * * Make sure the following errors are generated as specified: * * a) GL_INVALID_VALUE should be generated by: * I. glVertexAttribL1d () * II. glVertexAttribL2d () * III. glVertexAttribL3d () * IV. glVertexAttribL4d () * V. glVertexAttribL1dv () * VI. glVertexAttribL2dv () * VII. glVertexAttribL3dv () * VIII. glVertexAttribL4dv () * IX. glVertexAttribLPointer() * * if is greater than or equal to GL_MAX_VERTEX_ATTRIBS; * * b) GL_INVALID_ENUM should be generated by glVertexAttribLPointer() * if is not GL_DOUBLE; * * c) GL_INVALID_VALUE should be generated by glVertexAttribLPointer() * if is not 1, 2, 3 or 4. * * d) GL_INVALID_VALUE should be generated by glVertexAttribLPointer() * if is negative. * * e) GL_INVALID_OPERATION should be generated by glVertexAttribLPointer() * if zero is bound to the GL_ARRAY_BUFFER buffer object binding * point and the argument is not NULL. * * f) GL_INVALID_OPERATION should be generated by glGetVertexAttribLdv() * if is zero. **/ class ApiErrorsTest : public Base { public: /* Public methods */ ApiErrorsTest(deqp::Context& context); virtual ~ApiErrorsTest() { } /* Public methods inheritated from TestCase */ virtual void deinit(); virtual tcu::TestNode::IterateResult iterate(); private: /* Private methods */ void invalidEnum(bool& result); void invalidOperation(bool& result); void invalidValue(bool& result); void verifyError(GLenum expected_error, const char* function_name, int line_number, bool& result); /* Private fields */ GLuint m_vertex_array_object_id; }; /** Constructor * * @param context CTS context instance **/ ApiErrorsTest::ApiErrorsTest(deqp::Context& context) : Base(context, "api_errors", "Verify that API routines provoke errors as specified"), m_vertex_array_object_id(0) { /* Nothing to be done here */ } void ApiErrorsTest::deinit() { /* Delete VAO */ if (0 != m_vertex_array_object_id) { gl.bindVertexArray(0); gl.deleteVertexArrays(1, &m_vertex_array_object_id); m_vertex_array_object_id = 0; } } /** Execute test * * @return tcu::TestNode::STOP **/ tcu::TestNode::IterateResult ApiErrorsTest::iterate() { IterateStart(); bool result = true; RequireExtension("GL_ARB_vertex_attrib_64bit"); gl.genVertexArrays(1, &m_vertex_array_object_id); GLU_EXPECT_NO_ERROR(gl.getError(), "GenVertexArrays"); gl.bindVertexArray(m_vertex_array_object_id); GLU_EXPECT_NO_ERROR(gl.getError(), "BindVertexArray"); invalidEnum(result); invalidOperation(result); invalidValue(result); /* Done */ return IterateStop(result); } /** Test if GL_INVALID_ENUM error is provoked as expected * * @param result If test fails result is set to false, not modified otherwise. **/ void ApiErrorsTest::invalidEnum(bool& result) { /* *b) GL_INVALID_ENUM should be generated by glVertexAttribLPointer() * if is not GL_DOUBLE; */ static const GLenum type_array[] = { GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_INT, GL_UNSIGNED_INT, GL_HALF_FLOAT, GL_FLOAT, GL_FIXED, GL_INT_2_10_10_10_REV, GL_UNSIGNED_INT_2_10_10_10_REV, GL_UNSIGNED_INT_10F_11F_11F_REV }; static const GLuint type_array_length = sizeof(type_array) / sizeof(type_array[0]); for (GLuint i = 0; i < type_array_length; ++i) { const GLenum type = type_array[i]; std::stringstream message; message << "VertexAttribLPointer(..., " << glu::getTypeName(type) << " /* type */, ...)"; gl.vertexAttribLPointer(1 /*index */, 4 /*size */, type, 0 /* stride */, 0 /* pointer */); verifyError(GL_INVALID_ENUM, message.str().c_str(), __LINE__, result); } gl.vertexAttribLPointer(1 /* index */, 4 /*size */, GL_DOUBLE, 0 /* stride */, 0 /* pointer */); verifyError(GL_NO_ERROR, "VertexAttribLPointer(..., GL_DOUBLE /* type */, ...)", __LINE__, result); } /** Test if GL_INVALID_OPERATON error is provoked as expected * * @param result If test fails result is set to false, not modified otherwise. **/ void ApiErrorsTest::invalidOperation(bool& result) { /* *e) GL_INVALID_OPERATION should be generated by glVertexAttribLPointer() * if zero is bound to the GL_ARRAY_BUFFER buffer object binding * point and the argument is not NULL. */ static const GLvoid* pointer_array[] = { (GLvoid*)1, (GLvoid*)4, (GLvoid*)-16 }; static const GLuint pointer_array_length = sizeof(pointer_array) / sizeof(pointer_array[0]); gl.bindBuffer(GL_ARRAY_BUFFER, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "BindBuffer"); for (GLuint i = 0; i < pointer_array_length; ++i) { const GLvoid* pointer = pointer_array[i]; std::stringstream message; message << "VertexAttribLPointer(..., " << pointer << " /* pointer */)"; gl.vertexAttribLPointer(1 /* index */, 4 /*size */, GL_DOUBLE, 0 /* stride */, pointer); verifyError(GL_INVALID_OPERATION, message.str().c_str(), __LINE__, result); } } /** Test if GL_INVALID_VALUE error is provoked as expected * * @param result If test fails result is set to false, not modified otherwise. **/ void ApiErrorsTest::invalidValue(bool& result) { GLint max_vertex_attribs = GetMaxVertexAttribs(); const GLdouble vector[4] = { 0.0, 0.0, 0.0, 0.0 }; /* * a) GL_INVALID_VALUE should be generated by: * I. glVertexAttribL1d () * II. glVertexAttribL2d () * III. glVertexAttribL3d () * IV. glVertexAttribL4d () * V. glVertexAttribL1dv () * VI. glVertexAttribL2dv () * VII. glVertexAttribL3dv () * VIII. glVertexAttribL4dv () * IX. glVertexAttribLPointer() * * if is greater than or equal to GL_MAX_VERTEX_ATTRIBS; */ gl.vertexAttribL1d(max_vertex_attribs, 0.0); verifyError(GL_INVALID_VALUE, "VertexAttribL1d(GL_MAX_VERTEX_ATTRIBS, ...)", __LINE__, result); gl.vertexAttribL1d(max_vertex_attribs + 1, 0.0); verifyError(GL_INVALID_VALUE, "VertexAttribL1d(GL_MAX_VERTEX_ATTRIBS + 1, ...)", __LINE__, result); gl.vertexAttribL2d(max_vertex_attribs, 0.0, 0.0); verifyError(GL_INVALID_VALUE, "VertexAttribL2d(GL_MAX_VERTEX_ATTRIBS, ...)", __LINE__, result); gl.vertexAttribL2d(max_vertex_attribs + 1, 0.0, 0.0); verifyError(GL_INVALID_VALUE, "VertexAttribL2d(GL_MAX_VERTEX_ATTRIBS + 1, ...)", __LINE__, result); gl.vertexAttribL3d(max_vertex_attribs, 0.0, 0.0, 0.0); verifyError(GL_INVALID_VALUE, "VertexAttribL3d(GL_MAX_VERTEX_ATTRIBS, ...)", __LINE__, result); gl.vertexAttribL3d(max_vertex_attribs + 1, 0.0, 0.0, 0.0); verifyError(GL_INVALID_VALUE, "VertexAttribL3d(GL_MAX_VERTEX_ATTRIBS + 1, ...)", __LINE__, result); gl.vertexAttribL4d(max_vertex_attribs, 0.0, 0.0, 0.0, 0.0); verifyError(GL_INVALID_VALUE, "VertexAttribL4d(GL_MAX_VERTEX_ATTRIBS, ...)", __LINE__, result); gl.vertexAttribL4d(max_vertex_attribs + 1, 0.0, 0.0, 0.0, 0.0); verifyError(GL_INVALID_VALUE, "VertexAttribL4d(GL_MAX_VERTEX_ATTRIBS + 1, ...)", __LINE__, result); gl.vertexAttribL1dv(max_vertex_attribs, vector); verifyError(GL_INVALID_VALUE, "VertexAttribL1dv(GL_MAX_VERTEX_ATTRIBS, ...)", __LINE__, result); gl.vertexAttribL1dv(max_vertex_attribs + 1, vector); verifyError(GL_INVALID_VALUE, "VertexAttribL1dv(GL_MAX_VERTEX_ATTRIBS + 1, ...)", __LINE__, result); gl.vertexAttribL2dv(max_vertex_attribs, vector); verifyError(GL_INVALID_VALUE, "VertexAttribL2dv(GL_MAX_VERTEX_ATTRIBS, ...)", __LINE__, result); gl.vertexAttribL2dv(max_vertex_attribs + 1, vector); verifyError(GL_INVALID_VALUE, "VertexAttribL2dv(GL_MAX_VERTEX_ATTRIBS + 1, ...)", __LINE__, result); gl.vertexAttribL3dv(max_vertex_attribs, vector); verifyError(GL_INVALID_VALUE, "VertexAttribL3dv(GL_MAX_VERTEX_ATTRIBS, ...)", __LINE__, result); gl.vertexAttribL3dv(max_vertex_attribs + 1, vector); verifyError(GL_INVALID_VALUE, "VertexAttribL3dv(GL_MAX_VERTEX_ATTRIBS + 1, ...)", __LINE__, result); gl.vertexAttribL4dv(max_vertex_attribs, vector); verifyError(GL_INVALID_VALUE, "VertexAttribL4dv(GL_MAX_VERTEX_ATTRIBS, ...)", __LINE__, result); gl.vertexAttribL4dv(max_vertex_attribs + 1, vector); verifyError(GL_INVALID_VALUE, "VertexAttribL4dv(GL_MAX_VERTEX_ATTRIBS + 1, ...)", __LINE__, result); gl.vertexAttribLPointer(max_vertex_attribs, 4 /*size */, GL_DOUBLE, 0 /* stride */, 0 /* pointer */); verifyError(GL_INVALID_VALUE, "VertexAttribLPointer(GL_MAX_VERTEX_ATTRIBS, ...)", __LINE__, result); gl.vertexAttribLPointer(max_vertex_attribs + 1, 4 /*size */, GL_DOUBLE, 0 /* stride */, 0 /* pointer */); verifyError(GL_INVALID_VALUE, "VertexAttribLPointer(GL_MAX_VERTEX_ATTRIBS + 1, ...)", __LINE__, result); /* *c) GL_INVALID_VALUE should be generated by glVertexAttribLPointer() *if is not 1, 2, 3 or 4. */ gl.vertexAttribLPointer(1, 0 /*size */, GL_DOUBLE, 0 /* stride */, 0 /* pointer */); verifyError(GL_INVALID_VALUE, "VertexAttribLPointer(..., 0 /* size */, ...)", __LINE__, result); gl.vertexAttribLPointer(1, 5 /*size */, GL_DOUBLE, 0 /* stride */, 0 /* pointer */); verifyError(GL_INVALID_VALUE, "VertexAttribLPointer(..., 5 /* size */, ...)", __LINE__, result); /* *d) GL_INVALID_VALUE should be generated by glVertexAttribLPointer() * if is negative. */ gl.vertexAttribLPointer(1, 4 /*size */, GL_DOUBLE, -1 /* stride */, 0 /* pointer */); verifyError(GL_INVALID_VALUE, "VertexAttribLPointer(..., -1 /* stride */, ...)", __LINE__, result); gl.vertexAttribLPointer(1, 4 /*size */, GL_DOUBLE, -4 /* stride */, 0 /* pointer */); verifyError(GL_INVALID_VALUE, "VertexAttribLPointer(..., -4 /* stride */, ...)", __LINE__, result); } /** Verify that GetError returns expected error code. In case of failure logs error message. * * @param expected_error Expected error code * @param function_name Name of function to log in case of error * @param line_number Line number, for reference * @param result Result of verification, set to false in case of failure, not modified otherwise **/ void ApiErrorsTest::verifyError(GLenum expected_error, const char* function_name, int line_number, bool& result) { GLenum error = gl.getError(); if (expected_error != error) { m_log << tcu::TestLog::Section("Error", ""); m_log << tcu::TestLog::Message << "GetError returned: " << glu::getErrorStr(error) << tcu::TestLog::EndMessage; m_log << tcu::TestLog::Message << "Expected: " << glu::getErrorStr(expected_error) << tcu::TestLog::EndMessage; m_log << tcu::TestLog::Message << "Operation: " << function_name << tcu::TestLog::EndMessage; m_log << tcu::TestLog::Message << "File: " << __FILE__ << "@" << line_number << tcu::TestLog::EndMessage; m_log << tcu::TestLog::EndSection; result = false; } } /** Implementation of conformance test "2", description follows. * * Make sure that all available generic vertex attributes report * correct values when queried with corresponding glGetVertexAttribL*() * function, after they had been set with a glVertexAttribL*() call. * All double-precision floating-point setters and getters should * be checked, as enlisted below: * * * glVertexAttribL1d () * * glVertexAttribL2d () * * glVertexAttribL3d () * * glVertexAttribL4d () * * glVertexAttribL1dv() * * glVertexAttribL2dv() * * glVertexAttribL3dv() * * glVertexAttribL4dv() * * The test should also verify glGetVertexAttribiv() and * glGetVertexAttribLdv() report correct property values for all * vertex attribute arrays configured with glVertexAttribLPointer() * call. Two different configurations should be checked for each * VAA index. **/ class GetVertexAttribTest : public Base { public: /* Public constructor and destructor */ GetVertexAttribTest(deqp::Context& context); virtual ~GetVertexAttribTest() { } /* Public methods inheritated from TestCase */ virtual void deinit(); virtual tcu::TestNode::IterateResult iterate(); private: /* Private types */ /** Template class to store vertex attribute data * * @tparam SIZE Number of elements **/ template class vertexAttribute { public: vertexAttribute(GLdouble min, GLdouble max) { for (GLuint i = 0; i < SIZE; ++i) { m_array[i] = RandomDouble(min, max); } } GLdouble m_array[SIZE]; }; /* Private methods */ /* checkVertexAttrib methods */ template void checkVertexAttribLd(GLuint index, bool& result) const; template void checkVertexAttribLdv(GLuint index, bool& result) const; void checkVertexAttribLPointer(GLuint index, bool& result) const; /* Wrappers for vertexAttribLd routines */ template void vertexAttribLd(GLuint index, const vertexAttribute& attribute) const; template void vertexAttribLdv(GLuint index, const vertexAttribute& attribute) const; /* Utilities */ bool compareDoubles(const GLdouble* a, const GLdouble* b, GLuint length) const; void initTest(); bool verifyResults(GLuint index, GLenum pname, GLint expected_value) const; bool verifyResults(const GLdouble* set_values, GLuint length, GLuint index, const char* function_name, int line_number) const; bool verifyPointerResults(const GLdouble* set_values, GLuint length, GLuint index, int line_number) const; void logError(const GLdouble* set_values, const GLdouble* get_values, GLuint length, const char* function_name, GLuint index, int line_number) const; /* Private fields */ const GLdouble m_epsilon; static const GLuint m_n_iterations = 128; GLint m_max_vertex_attribs; const GLdouble m_min; const GLdouble m_max; /* GL objects */ GLuint m_buffer_object_id; GLuint m_vertex_array_object_id; }; /** Constructor * * @param context CTS context **/ GetVertexAttribTest::GetVertexAttribTest(deqp::Context& context) : Base(context, "get_vertex_attrib", "Verify that GetVertexAttribL* routines") , m_epsilon(0.0) , m_max_vertex_attribs(0) , m_min(-16.384) , m_max(16.384) , m_buffer_object_id(0) , m_vertex_array_object_id(0) { /* Nothing to be done */ } /** Clean up after test * **/ void GetVertexAttribTest::deinit() { if (0 != m_buffer_object_id) { gl.bindBuffer(GL_ARRAY_BUFFER, 0); gl.deleteBuffers(1, &m_buffer_object_id); m_buffer_object_id = 0; } if (0 != m_vertex_array_object_id) { gl.bindVertexArray(0); gl.deleteVertexArrays(1, &m_vertex_array_object_id); m_vertex_array_object_id = 0; } } /** Execute test * * @return tcu::TestNode::STOP **/ tcu::TestNode::IterateResult GetVertexAttribTest::iterate() { IterateStart(); bool result = true; RequireExtension("GL_ARB_vertex_attrib_64bit"); initTest(); for (GLint i = 1; i < m_max_vertex_attribs; ++i) { checkVertexAttribLd<1>(i, result); checkVertexAttribLd<2>(i, result); checkVertexAttribLd<3>(i, result); checkVertexAttribLd<4>(i, result); checkVertexAttribLdv<1>(i, result); checkVertexAttribLdv<2>(i, result); checkVertexAttribLdv<3>(i, result); checkVertexAttribLdv<4>(i, result); checkVertexAttribLPointer(i, result); } /* Done */ return IterateStop(result); } /** Verifies glVertexAttribLd routines * * @tparam SIZE Size of vertex attribute * * @param index Index of vertex attribute, starts from 1. * @param result Result of verification, set to false in case of failure, not modified otherwise. **/ template void GetVertexAttribTest::checkVertexAttribLd(GLuint index, bool& result) const { std::stringstream function_name; function_name << "VertexAttribL" << SIZE << "d"; for (GLuint i = 0; i < m_n_iterations; ++i) { vertexAttribute vertex_attribute(m_min, m_max); vertexAttribLd(index, vertex_attribute); GLU_EXPECT_NO_ERROR(gl.getError(), function_name.str().c_str()); if (false == verifyResults(vertex_attribute.m_array, SIZE, index, function_name.str().c_str(), __LINE__)) { result = false; return; } } } /** Verifies glVertexAttribLdv routines * * @tparam SIZE Size of vertex attribute * * @param index Index of vertex attribute, starts from 1. * @param result Result of verification, set to false in case of failure, not modified otherwise. **/ template void GetVertexAttribTest::checkVertexAttribLdv(GLuint index, bool& result) const { std::stringstream function_name; function_name << "VertexAttribL" << SIZE << "dv"; for (GLuint i = 0; i < m_n_iterations; ++i) { vertexAttribute vertex_attribute(m_min, m_max); vertexAttribLdv(index, vertex_attribute); GLU_EXPECT_NO_ERROR(gl.getError(), function_name.str().c_str()); if (false == verifyResults(vertex_attribute.m_array, SIZE, index, function_name.str().c_str(), __LINE__)) { result = false; return; } } } /** Verifies glVertexAttribLPointer * * @param index Index of vertex attribute, starts from 1. * @param result Result of verification, set to false in case of failure, not modified otherwise. **/ void GetVertexAttribTest::checkVertexAttribLPointer(GLuint index, bool& result) const { static const GLuint max_size = 4; static const GLuint max_stride = 16; gl.bindVertexArray(m_vertex_array_object_id); GLU_EXPECT_NO_ERROR(gl.getError(), "BindVertexArray"); gl.bindBuffer(GL_ARRAY_BUFFER, m_buffer_object_id); GLU_EXPECT_NO_ERROR(gl.getError(), "BindBuffer"); for (GLuint size = 1; size <= max_size; ++size) { for (GLuint stride = 0; stride < max_stride; ++stride) { gl.bindBuffer(GL_ARRAY_BUFFER, m_buffer_object_id); GLU_EXPECT_NO_ERROR(gl.getError(), "BindBuffer"); gl.vertexAttribLPointer(index, size, GL_DOUBLE, stride, (GLvoid*)0); GLU_EXPECT_NO_ERROR(gl.getError(), "VertexAttribLPointer"); gl.bindBuffer(GL_ARRAY_BUFFER, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "BindBuffer"); if (false == verifyResults(index, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, m_buffer_object_id)) { result = false; } if (false == verifyResults(index, GL_VERTEX_ATTRIB_ARRAY_ENABLED, GL_FALSE)) { result = false; } if (false == verifyResults(index, GL_VERTEX_ATTRIB_ARRAY_SIZE, size)) { result = false; } if (false == verifyResults(index, GL_VERTEX_ATTRIB_ARRAY_STRIDE, stride)) { result = false; } if (false == verifyResults(index, GL_VERTEX_ATTRIB_ARRAY_TYPE, GL_DOUBLE)) { result = false; } if (false == verifyResults(index, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, GL_FALSE)) { result = false; } if (false == verifyResults(index, GL_VERTEX_ATTRIB_ARRAY_INTEGER, GL_FALSE)) { result = false; } if (false == verifyResults(index, GL_VERTEX_ATTRIB_ARRAY_DIVISOR, 0)) { result = false; } } } } /** Wrapper of vertexAttribLd routines. * * @tparam SIZE Size of vertex attribute. Specialisation for 1. * * @param index Index parameter * @param attribute Vertex attribute data are taken from provided instance of vertexAttribute **/ template <> void GetVertexAttribTest::vertexAttribLd<1>(GLuint index, const GetVertexAttribTest::vertexAttribute<1>& attribute) const { gl.vertexAttribL1d(index, attribute.m_array[0]); } /** Wrapper of vertexAttribLd routines. * * @tparam SIZE Size of vertex attribute. Specialisation for 2. * * @param index Index parameter * @param attribute Vertex attribute data are taken from provided instance of vertexAttribute **/ template <> void GetVertexAttribTest::vertexAttribLd<2>(GLuint index, const GetVertexAttribTest::vertexAttribute<2>& attribute) const { gl.vertexAttribL2d(index, attribute.m_array[0], attribute.m_array[1]); } /** Wrapper of vertexAttribLd routines. * * @tparam SIZE Size of vertex attribute. Specialisation for 3. * * @param index Index parameter * @param attribute Vertex attribute data are taken from provided instance of vertexAttribute **/ template <> void GetVertexAttribTest::vertexAttribLd<3>(GLuint index, const GetVertexAttribTest::vertexAttribute<3>& attribute) const { gl.vertexAttribL3d(index, attribute.m_array[0], attribute.m_array[1], attribute.m_array[2]); } /** Wrapper of vertexAttribLd routines. * * @tparam SIZE Size of vertex attribute. Specialisation for 4. * * @param index Index parameter * @param attribute Vertex attribute data are taken from provided instance of vertexAttribute **/ template <> void GetVertexAttribTest::vertexAttribLd<4>(GLuint index, const GetVertexAttribTest::vertexAttribute<4>& attribute) const { gl.vertexAttribL4d(index, attribute.m_array[0], attribute.m_array[1], attribute.m_array[2], attribute.m_array[3]); } /** Wrapper of vertexAttribLdv routines. * * @tparam SIZE Size of vertex attribute. Specialisation for 1. * * @param index Index parameter * @param attribute Vertex attribute data are taken from provided instance of vertexAttribute **/ template <> void GetVertexAttribTest::vertexAttribLdv<1>(GLuint index, const GetVertexAttribTest::vertexAttribute<1>& attribute) const { gl.vertexAttribL1dv(index, attribute.m_array); } /** Wrapper of vertexAttribLdv routines. * * @tparam SIZE Size of vertex attribute. Specialisation for 2. * * @param index Index parameter * @param attribute Vertex attribute data are taken from provided instance of vertexAttribute **/ template <> void GetVertexAttribTest::vertexAttribLdv<2>(GLuint index, const GetVertexAttribTest::vertexAttribute<2>& attribute) const { gl.vertexAttribL2dv(index, attribute.m_array); } /** Wrapper of vertexAttribLdv routines. * * @tparam SIZE Size of vertex attribute. Specialisation for 3. * * @param index Index parameter * @param attribute Vertex attribute data are taken from provided instance of vertexAttribute **/ template <> void GetVertexAttribTest::vertexAttribLdv<3>(GLuint index, const GetVertexAttribTest::vertexAttribute<3>& attribute) const { gl.vertexAttribL3dv(index, attribute.m_array); } /** Wrapper of vertexAttribLdv routines. * * @tparam SIZE Size of vertex attribute. Specialisation for 4. * * @param index Index parameter * @param attribute Vertex attribute data are taken from provided instance of vertexAttribute **/ template <> void GetVertexAttribTest::vertexAttribLdv<4>(GLuint index, const GetVertexAttribTest::vertexAttribute<4>& attribute) const { gl.vertexAttribL4dv(index, attribute.m_array); } /** Compare two arrays of doubles * * @param a First array of doubles * @param b Second array of doubles * @param length Length of arrays * * @return true if arrays are considered equal, false otherwise **/ bool GetVertexAttribTest::compareDoubles(const GLdouble* a, const GLdouble* b, GLuint length) const { for (GLuint i = 0; i < length; ++i) { if ((b[i] > a[i] + m_epsilon) || (b[i] < a[i] - m_epsilon)) { return false; } } return true; } /** Prepare buffer and vertex array object, get max vertex attributes * **/ void GetVertexAttribTest::initTest() { gl.genBuffers(1, &m_buffer_object_id); GLU_EXPECT_NO_ERROR(gl.getError(), "GenBuffers"); gl.bindBuffer(GL_ARRAY_BUFFER, m_buffer_object_id); GLU_EXPECT_NO_ERROR(gl.getError(), "BindBuffer"); gl.bufferData(GL_ARRAY_BUFFER, 16 * sizeof(GLdouble), 0, GL_DYNAMIC_DRAW); GLU_EXPECT_NO_ERROR(gl.getError(), "BufferStorage"); gl.genVertexArrays(1, &m_vertex_array_object_id); GLU_EXPECT_NO_ERROR(gl.getError(), "GenVertexArrays"); m_max_vertex_attribs = GetMaxVertexAttribs(); } /** Logs message informing that values got with GetVertexAttribLdv do not match set with "function_name" * * @param set_values Values set with "function_name" * @param get_values Values extracted with GetVertexAttribLdv * @param length Length of "get/set_values" arrays * @param function_name Name of function used to set vertex attributes * @param index Index of vertex attribute * @param line_number Line number refereing to location of "function_name" **/ void GetVertexAttribTest::logError(const GLdouble* set_values, const GLdouble* get_values, GLuint length, const char* function_name, GLuint index, int line_number) const { m_log << tcu::TestLog::Section("Error", ""); tcu::MessageBuilder message = m_log << tcu::TestLog::Message; message << "Values set with " << function_name << " ["; for (GLuint i = 0; i < length; ++i) { message << std::setprecision(24) << set_values[i]; if (length != i + 1) { message << ", "; } } message << "]" << tcu::TestLog::EndMessage; message = m_log << tcu::TestLog::Message; message << "Values got with GetVertexAttribLdv" << " ["; for (GLuint i = 0; i < length; ++i) { message << std::setprecision(24) << get_values[i]; if (length != i + 1) { message << ", "; } } message << "]" << tcu::TestLog::EndMessage; m_log << tcu::TestLog::Message << "Index: " << index << tcu::TestLog::EndMessage; m_log << tcu::TestLog::Message << "File: " << __FILE__ << "@" << line_number << tcu::TestLog::EndMessage; m_log << tcu::TestLog::EndSection; } /** Verify results of vertexAttribLPointer * * @param index Index of vertex attribute * @param pname Parameter name to be querried with getVertexAttribiv and getVertexAttribLdv * @param expected_value Expected valued * * @return true if Results match expected_value, false otherwise **/ bool GetVertexAttribTest::verifyResults(GLuint index, GLenum pname, GLint expected_value) const { GLint params_getVertexAttribiv = 0; GLdouble params_getVertexAttribLdv = 0.0; gl.getVertexAttribiv(index, pname, ¶ms_getVertexAttribiv); GLU_EXPECT_NO_ERROR(gl.getError(), "GetVertexAttribiv"); gl.getVertexAttribLdv(index, pname, ¶ms_getVertexAttribLdv); GLU_EXPECT_NO_ERROR(gl.getError(), "GetVertexAttribLdv"); if ((expected_value != params_getVertexAttribiv) || (expected_value != params_getVertexAttribLdv)) { m_log << tcu::TestLog::Section("Error", ""); m_log << tcu::TestLog::Message << "GetVertexAttribiv(" << index << "/* index */, " << glu::getVertexAttribParameterNameName(pname) << "/* pname */)" << tcu::TestLog::EndMessage; m_log << tcu::TestLog::Message << "Result: " << params_getVertexAttribiv << tcu::TestLog::EndMessage; m_log << tcu::TestLog::Message << "GetVertexAttribLdv(" << index << "/* index */, " << glu::getVertexAttribParameterNameName(pname) << "/* pname */)" << tcu::TestLog::EndMessage; m_log << tcu::TestLog::Message << "Result: " << params_getVertexAttribLdv << tcu::TestLog::EndMessage; m_log << tcu::TestLog::Message << "Expected: " << expected_value << tcu::TestLog::EndMessage; m_log << tcu::TestLog::Message << "File: " << __FILE__ << "@" << __LINE__ << tcu::TestLog::EndMessage; m_log << tcu::TestLog::EndSection; return false; } return true; } /** Verify results of vertexAttribLdv routines * * @param set_values Values set with vertexAttribLdv * @param length Length of "set_values" array * @param index Index of vertex attribute * @param function_name Name of function used to set, it will be used for error logging * @param line_number Line number refering to location of "function_name", used to log errors * * @return true if results match set values, false otherwise **/ bool GetVertexAttribTest::verifyResults(const GLdouble* set_values, GLuint length, GLuint index, const char* function_name, int line_number) const { GLdouble results[4] = { 0.0, 0.0, 0.0, 0.0 }; gl.getVertexAttribLdv(index, GL_CURRENT_VERTEX_ATTRIB, results); GLU_EXPECT_NO_ERROR(gl.getError(), "GetVertexAttribLdv"); if (false == compareDoubles(set_values, results, length)) { logError(set_values, results, length, function_name, index, line_number); return false; } return true; } /** Implementation of conformance test "3", description follows. * * Verify that a total of GL_MAX_VERTEX_ATTRIBS double and dvec2, * (GL_MAX_VERTEX_ATTRIBS / 2) dvec3, dvec4 and dmat2, * (GL_MAX_VERTEX_ATTRIBS / 3) dmat3x2, * (GL_MAX_VERTEX_ATTRIBS / 4) dmat4x2, dmat2x3 and dmat2x4, * (GL_MAX_VERTEX_ATTRIBS / 6) dmat3 and dmat3x4, * (GL_MAX_VERTEX_ATTRIBS / 8) dmat4x3 and dmat4, * attributes can be used in each shader stage at the same time. * * The test should run in 7 iterations: * * a) In the first iteration, (GL_MAX_VERTEX_ATTRIBS / 2) double * attributes and (GL_MAX_VERTEX_ATTRIBS / 2) dvec2 attributes * should be defined in a vertex shader. The test should verify * the values exposed by these attributes and write 1 to an * output variable if all attribute values are found to be * correct, or set it to 0 if at least one of the retrieved * values is found invalid. * * Double attributes should be assigned the value: * (n_attribute + gl_VertexID * 2) * * Dvec2 attribute components should be assigned the following * vector values: * (n_attribute + gl_VertexID * 3 + 1, * n_attribute + gl_VertexID * 3 + 2) * * b) In the second iteration, (GL_MAX_VERTEX_ATTRIBS / 4) dvec3 * and (GL_MAX_VERTEX_ATTRIBS / 4) dvec4 attributes should be * defined in a vertex shader. Verification of the data exposed * by these input variables should be performed as in step a), * with an exception of the values passed through the attributes. * * Dvec3 attribute components should be assigned the following * vector values: * (n_attribute + gl_VertexID * 3 + 0, * n_attribute + gl_VertexID * 3 + 1, * n_attribute + gl_VertexID * 3 + 2). * * Dvec4 attribute components should be assigned the following * vector values: * (n_attribute + gl_VertexID * 4 + 0, * n_attribute + gl_VertexID * 4 + 1, * n_attribute + gl_VertexID * 4 + 2, * n_attribute + gl_VertexID * 4 + 3). * * n_attribute corresponds to the ordinal number of each attribute, * as defined in the shader. * * c) In the third iteration, (GL_MAX_VERTEX_ATTRIBS / 2) dmat2 attributes * should be defined in a vertex shader. Verification of the data exposed * by these input variables should be performed as in step a), with an * exception of the values passed through the attributes. * * Subsequent matrix elements should be assigned the following value: * (n_type + n_attribute + gl_VertexID * 16 + n_value) * * n_type corresponds to the ordinal number of type as per the * order at the beginning of the paragraph. * n_value corresponds to the ordinal number of the element. * * d) In the fourth iteration, (GL_MAX_VERTEX_ATTRIBS / 8) dmat3x2 and * (GL_MAX_VERTEX_ATTRIBS / 8) dmat4x2 attributes should be defined in a * vertex shader. Verification of the data exposed by these input * variables should be performed as in step a), with an exception of the * values passed through the attributes. * * Use the same element values as in step c) * * e) In the fifth iteration, (GL_MAX_VERTEX_ATTRIBS / 8) dmat2x3 and * (GL_MAX_VERTEX_ATTRIBS / 8) dmat2x4 attributes should be defined in a * vertex shader. Verification of the data exposed by these input * variables should be performed as in step a), with an exception of the * values passed through the attributes. * * Use the same element values as in step c) * * f) In the sixth iteration, (GL_MAX_VERTEX_ATTRIBS / 12) dmat3 and * (GL_MAX_VERTEX_ATTRIBS / 12) dmat3x4 attributes should be defined in a * vertex shader. Verification of the data exposed by these input * variables should be performed as in step a), with an exception of the * values passed through the attributes. * * Use the same element values as in step c) * * g) In the seventh iteration, (GL_MAX_VERTEX_ATTRIBS / 16) dmat4x3 and * (GL_MAX_VERTEX_ATTRIBS / 16) dmat4 attributes should be defined in a * vertex shader. Verification of the data exposed by these input * variables should be performed as in step a), with an exception of the * values passed through the attributes. * * Use the same element values as in step c) * * h) Modify the language of cases a) - g), so that instead of separate * attributes, all attributes of the same type are now a single arrayed * attribute. * * Vertex shaders from both iterations should be used to form two program * objects. 1024 vertices should be used for a non-indiced GL_POINTS * draw call, made using those two programs. * * All glVertexAttribL*() and glVertexAttribLPointer() should be used for * the purpose of the test. The following draw call API functions should be * tested: * * a) glDrawArrays() * b) glDrawArraysInstanced(), primcount > 1, zero vertex attrib divisor * c) glDrawArraysInstanced(), primcount > 1, non-zero vertex attrib divisor * d) glDrawElements() * e) glDrawElementsInstanced(), properties as in b) * f) glDrawElementsInstanced(), properties as in c) * * All shaders used by the test should come in two flavors: * * - one where attribute locations are explicitly defined in the body; * - the other one where attribute locations are to be assigned by * the compiler. * * For each shader, the test should make sure that all attributes have * been assigned correct amount of locations. (eg: dvec4 attribute * should be granted exactly one location). * * Data stored in output variables should be XFBed to the test. * The test passes if the retrieved values are found to be valid * for all vertex shader invocations. **/ class LimitTest : public Base { public: /* Public constructor and destructor */ LimitTest(deqp::Context& context); virtual ~LimitTest() { } /* Public methods inheritated from TestCase */ virtual void deinit(); virtual tcu::TestNode::IterateResult iterate(); private: /* Private types */ class programInfo { public: programInfo(const glw::Functions& gl); ~programInfo(); GLuint m_fragment_shader_id; GLuint m_program_id; GLuint m_vertex_shader_id; private: const glw::Functions& gl; }; struct attributeConfiguration { attributeConfiguration() : m_n_attributes_per_group(0) , m_n_elements(0) , m_n_rows(0) , m_n_types(0) , m_type_names(0) , m_vertex_length(0) { /* nothing to be done */ } GLint m_n_attributes_per_group; const GLint* m_n_elements; const GLint* m_n_rows; GLint m_n_types; const GLchar* const* m_type_names; GLint m_vertex_length; }; typedef GLint _varyingType; /* Private enums */ enum _iteration { DOUBLE_DVEC2, // 1 + 1 = 2 DVEC3_DVEC4, // 2 + 2 = 4 DMAT2, // 2 * 1 = 2 DMAT3X2_DMAT4X2, // 3 * 1 + 4 * 1 = 8 DMAT2X3_DMAT2X4, // 2 * 2 + 2 * 2 = 8 DMAT3_DMAT3X4, // 3 * 2 + 3 * 2 = 12 DMAT4X3_DMAT4 // 4 * 2 + 4 * 2 = 16 }; enum _attributeType { REGULAR, PER_INSTANCE, CONSTANT, }; /*Private methods */ GLint calculateAttributeGroupOffset(const attributeConfiguration& configuration, GLint index) const; GLint calculateAttributeLocation(const attributeConfiguration& configuration, GLint attribute, GLint n_type) const; void calculateVertexLength(attributeConfiguration& configuration) const; void configureAttribute(_iteration iteration, const attributeConfiguration& configuration, GLint n_type, GLuint program_id, bool use_arrays, bool use_vertex_array) const; void getProgramDetails(_iteration iteration, bool use_arrays, bool use_locations, bool use_vertex_attrib_divisor, std::string& out_varying_name, std::string& out_vertex_shader_code) const; void getVertexArrayConfiguration(_iteration iteration, attributeConfiguration& out_configuration) const; void logTestIterationAndConfig(_iteration iteration, _attributeType attribute_type, bool use_arrays, bool use_locations) const; void prepareProgram(_iteration iteration, bool use_arrays, bool use_locations, bool use_vertex_attrib_divisor, programInfo& programInfo); void prepareVertexArray(_iteration iteration, _attributeType attribute_type, GLuint program_id, bool use_arrays) const; void prepareVertexArrayBuffer(_iteration iteration); void setAttributes(_iteration iteration, const attributeConfiguration& configuration, GLuint vertex, std::vector& out_buffer_data) const; void setAttributes_a(const attributeConfiguration& configuration, GLint n_type, GLuint vertex, std::vector& out_buffer_data) const; void setAttributes_a_scalar(const attributeConfiguration& configuration, GLint n_type, GLuint vertex, std::vector& out_buffer_data) const; void setAttributes_a_vec(const attributeConfiguration& configuration, GLint n_type, GLuint vertex, std::vector& out_buffer_data) const; void setAttributes_b(const attributeConfiguration& configuration, GLint n_type, GLuint vertex, std::vector& out_buffer_dataa) const; void setAttributes_c(const attributeConfiguration& configuration, GLint n_type, GLuint vertex, std::vector& out_buffer_data) const; bool testDrawArrays() const; bool testDrawArraysInstanced() const; bool testDrawElements() const; bool testDrawElementsInstanced() const; void testInit(); bool testIteration(_iteration iteration); bool testProgram(_iteration iteration, GLuint program_id, bool use_arrays) const; bool testProgramWithConstant(_iteration iteration, GLuint program_id, bool use_arrays) const; bool testProgramWithDivisor(_iteration iteration, GLuint program_id, bool use_arrays) const; bool verifyResult(bool use_instancing) const; /* Private fields */ /* Constants */ static const GLint m_array_attribute = -1; static const GLuint m_n_instances = 16; static const GLuint m_n_varyings = 1; static const GLuint m_n_vertices = 1024; static const GLuint m_transform_feedback_buffer_size = sizeof(_varyingType) * m_n_instances * m_n_vertices * m_n_varyings; /* GL objects */ GLuint m_element_array_buffer_id; GLuint m_transoform_feedback_buffer_id; GLuint m_vertex_array_buffer_id; GLuint m_vertex_array_object_id; }; /** Constructor * **/ LimitTest::programInfo::programInfo(const glw::Functions& gl_functions) : m_fragment_shader_id(0), m_program_id(0), m_vertex_shader_id(0), gl(gl_functions) { /* Nothing to be done here */ } /** Destructor * **/ LimitTest::programInfo::~programInfo() { if (0 != m_program_id) { gl.deleteProgram(m_program_id); m_program_id = 0; } if (0 != m_fragment_shader_id) { gl.deleteShader(m_fragment_shader_id); m_fragment_shader_id = 0; } if (0 != m_vertex_shader_id) { gl.deleteShader(m_vertex_shader_id); m_vertex_shader_id = 0; } } /** Constructor * * @param context CTS context **/ LimitTest::LimitTest(deqp::Context& context) : Base(context, "limits_test", "Verify that maximum allowed number of attribiutes can be used") , m_element_array_buffer_id(0) , m_transoform_feedback_buffer_id(0) , m_vertex_array_buffer_id(0) , m_vertex_array_object_id(0) { /* Nothing to be done here */ } /** Clean up after test * **/ void LimitTest::deinit() { /* Restore default settings */ if (0 != gl.disable) { gl.disable(GL_RASTERIZER_DISCARD); } /* Delete GL objects */ if (0 != m_element_array_buffer_id) { gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); gl.deleteBuffers(1, &m_element_array_buffer_id); m_element_array_buffer_id = 0; } if (0 != m_transoform_feedback_buffer_id) { gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0); gl.deleteBuffers(1, &m_transoform_feedback_buffer_id); m_transoform_feedback_buffer_id = 0; } if (0 != m_vertex_array_buffer_id) { gl.bindBuffer(GL_ARRAY_BUFFER, 0); gl.deleteBuffers(1, &m_vertex_array_buffer_id); m_vertex_array_buffer_id = 0; } if (0 != m_vertex_array_object_id) { gl.bindVertexArray(0); gl.deleteVertexArrays(1, &m_vertex_array_object_id); m_vertex_array_object_id = 0; } } /** Execute test * * @return tcu::TestNode::STOP **/ tcu::TestNode::IterateResult LimitTest::iterate() { IterateStart(); bool result = true; RequireExtension("GL_ARB_vertex_attrib_64bit"); testInit(); if (false == testIteration(DOUBLE_DVEC2)) { result = false; } if (false == testIteration(DVEC3_DVEC4)) { result = false; } if (false == testIteration(DMAT2)) { result = false; } if (false == testIteration(DMAT3X2_DMAT4X2)) { result = false; } if (false == testIteration(DMAT2X3_DMAT2X4)) { result = false; } if (false == testIteration(DMAT3_DMAT3X4)) { result = false; } if (false == testIteration(DMAT4X3_DMAT4)) { result = false; } /* Done */ return IterateStop(result); } /** Calculate offset of "n_type" attributes group in doubles, tightly packed, for vertex buffer offsets * * @param configuration Attribute configuration * @param n_type Attribute type ordinal number * * @return Calculated offset **/ GLint LimitTest::calculateAttributeGroupOffset(const attributeConfiguration& configuration, GLint n_type) const { GLint result = 0; for (GLint i = 0; i < n_type; ++i) { result += configuration.m_n_attributes_per_group * configuration.m_n_elements[i]; } return result; } /** Calculates attribute location for manually setting "layout(location =)". * Results are in reveresed order of vertex buffer * * @param configuration Attribute configuration * @param attribute Intex of attribute in "n_type" group * @param n_type Ordinal number of type * * @return Calculated location **/ GLint LimitTest::calculateAttributeLocation(const attributeConfiguration& configuration, GLint attribute, GLint n_type) const { const GLint n_types = configuration.m_n_types; GLint result = 0; /* Amount of location required for types after given "n_type" */ for (GLint i = n_types - 1; i > n_type; --i) { const GLint n_elements = configuration.m_n_elements[i]; const GLint n_rows = configuration.m_n_rows[i]; const GLint n_columns = n_elements / n_rows; result += n_columns * configuration.m_n_attributes_per_group; } /* Amount of locations required for attributes after given attribute in given "n_type" */ /* Arrayed attributes does not have any attributes after */ if (m_array_attribute != attribute) { const GLint n_elements = configuration.m_n_elements[n_type]; const GLint n_rows = configuration.m_n_rows[n_type]; const GLint n_columns = n_elements / n_rows; result += n_columns * (configuration.m_n_attributes_per_group - 1 - attribute); } /* Done */ return result; } /** Calculate vertex length in "doubles", tightly packed, for offset in vertex buffer * * @param configuration Attribute configuration, result is store as field ::m_vertex_length **/ void LimitTest::calculateVertexLength(attributeConfiguration& configuration) const { GLint result = 0; for (GLint i = 0; i < configuration.m_n_types; ++i) { result += configuration.m_n_elements[i] * configuration.m_n_attributes_per_group; } configuration.m_vertex_length = result; } /** Configure attributes in given "n_type" group * * @param iteration Iteration id * @param configuration Configuration of attributes * @param n_type "n_type" of attibutes * @param program_id Program object id * @param use_arrays If attributes are groupd in arrays * @param use_vertex_array If attributes are configured with vertex array or as constants **/ void LimitTest::configureAttribute(_iteration iteration, const attributeConfiguration& configuration, GLint n_type, GLuint program_id, bool use_arrays, bool use_vertex_array) const { static const GLint invalid_attrib_location = -1; const GLint attributes_index = n_type * configuration.m_n_attributes_per_group; const GLint group_offset = calculateAttributeGroupOffset(configuration, n_type); const GLint n_elements = configuration.m_n_elements[n_type]; const GLint n_rows = configuration.m_n_rows[n_type]; const GLint n_columns = n_elements / n_rows; const GLint vertex_length = configuration.m_vertex_length; /* For each attribute in "n_type" group */ for (GLint i = 0; i < configuration.m_n_attributes_per_group; ++i) { const GLint attribute_ordinal = i + attributes_index; std::stringstream attribute_name; /* Separate attributes are called: attribute_ORDINAL, arrayed: attribute_N_TYPE[INDEX] */ if (false == use_arrays) { attribute_name << "attribute_" << attribute_ordinal; } else { attribute_name << "attribute_" << n_type << "[" << i << "]"; } /* get location */ GLint attribute_location = gl.getAttribLocation(program_id, attribute_name.str().c_str()); GLU_EXPECT_NO_ERROR(gl.getError(), "GetAttribLocation"); if (invalid_attrib_location == attribute_location) { m_log << tcu::TestLog::Message << "GetAttribLocation(" << program_id << ", " << attribute_name.str() << ") returned: " << attribute_location << tcu::TestLog::EndMessage; TCU_FAIL("Inactive attribute"); } /* Configure */ if (true == use_vertex_array) { /* With vertex array */ for (GLint column = 0; column < n_columns; ++column) { const GLint attribute_offset = group_offset + i * n_elements; const GLint column_offset = column * n_rows; gl.enableVertexAttribArray(attribute_location + column); GLU_EXPECT_NO_ERROR(gl.getError(), "EnableVertexAttribArray"); gl.vertexAttribLPointer(attribute_location + column, n_rows /* size */, GL_DOUBLE, static_cast(vertex_length * sizeof(GLdouble)), (GLvoid*)((attribute_offset + column_offset) * sizeof(GLdouble))); GLU_EXPECT_NO_ERROR(gl.getError(), "VertexAttribLPointer"); } } else { /* As constant */ for (GLint column = 0; column < n_columns; ++column) { switch (iteration) { case DOUBLE_DVEC2: /* Double attributes should be assigned the value: (n_attribute + gl_VertexID * 2) */ /* Dvec2 attribute components should be assigned the following vector values: (n_attribute + gl_VertexID * 3 + 1, n_attribute + gl_VertexID * 3 + 2)*/ if (1 == n_rows) { gl.vertexAttribL1d(attribute_location, attribute_ordinal); } else { gl.vertexAttribL2d(attribute_location, attribute_ordinal + 1, attribute_ordinal + 2); } break; case DVEC3_DVEC4: /* Dvec3 attribute components should be assigned the following vector values: (n_attribute + gl_VertexID * 3 + 0, n_attribute + gl_VertexID * 3 + 1, n_attribute + gl_VertexID * 3 + 2). Dvec4 attribute components should be assigned the following vector values: (n_attribute + gl_VertexID * 4 + 0, n_attribute + gl_VertexID * 4 + 1, n_attribute + gl_VertexID * 4 + 2, n_attribute + gl_VertexID * 4 + 3).*/ if (3 == n_rows) { gl.vertexAttribL3d(attribute_location, attribute_ordinal + 0, attribute_ordinal + 1, attribute_ordinal + 2); } else { gl.vertexAttribL4d(attribute_location, attribute_ordinal + 0, attribute_ordinal + 1, attribute_ordinal + 2, attribute_ordinal + 3); } break; case DMAT2: case DMAT3X2_DMAT4X2: case DMAT2X3_DMAT2X4: case DMAT3_DMAT3X4: case DMAT4X3_DMAT4: /* Subsequent matrix elements should be assigned the following value: (n_type + n_attribute + gl_VertexID * 16 + n_value)*/ if (2 == n_rows) { gl.vertexAttribL2d(attribute_location + column, n_type + attribute_ordinal + 0 + column * n_rows, n_type + attribute_ordinal + 1 + column * n_rows); } else if (3 == n_rows) { gl.vertexAttribL3d(attribute_location + column, n_type + attribute_ordinal + 0 + column * n_rows, n_type + attribute_ordinal + 1 + column * n_rows, n_type + attribute_ordinal + 2 + column * n_rows); } else { gl.vertexAttribL4d(attribute_location + column, n_type + attribute_ordinal + 0 + column * n_rows, n_type + attribute_ordinal + 1 + column * n_rows, n_type + attribute_ordinal + 2 + column * n_rows, n_type + attribute_ordinal + 3 + column * n_rows); } break; } } } } } /** Get varying name and vertex shader code for given configuration * * @param iteration Iteration id * @param use_arrays If attributes should be grouped in arrays * @param use_locations If attributes locations should be set manualy * @param use_vertex_attrib_divisor If vertex attribute divisor should be used * @param out_varying_name Name of varying to be captured with transform feedback * @param out_vertex_shader_code Source code of vertex shader **/ void LimitTest::getProgramDetails(_iteration iteration, bool use_arrays, bool use_locations, bool use_vertex_attrib_divisor, std::string& out_varying_name, std::string& out_vertex_shader_code) const { static const GLchar* varying_name = "vs_output_value"; attributeConfiguration configuration; GLint n_attributes = 0; GLint n_types = 0; std::stringstream stream; const GLchar* advancement_str = (true == use_vertex_attrib_divisor) ? "gl_InstanceID" : "gl_VertexID"; getVertexArrayConfiguration(iteration, configuration); n_attributes = configuration.m_n_attributes_per_group; n_types = configuration.m_n_types; /* Preamble */ stream << "#version 400\n" "#extension GL_ARB_vertex_attrib_64bit : require\n" "\n" "precision highp float;\n" "\n"; /* Attribute declarations */ /* Spearated attributes are called: attribute_ORDINAL, arrayed: attribute_N_TYPE */ for (GLint n_type = 0; n_type < n_types; ++n_type) { const GLint attribute_offset = n_type * n_attributes; const GLchar* type_name = configuration.m_type_names[n_type]; stream << "// " << type_name << "\n"; if (false == use_arrays) { for (GLint attribute = 0; attribute < n_attributes; ++attribute) { if (true == use_locations) { const GLint location = calculateAttributeLocation(configuration, attribute, n_type); stream << "layout(location = " << location << ") "; } stream << "in " << type_name << " attribute_" << attribute + attribute_offset << ";\n"; } } else { if (true == use_locations) { const GLint location = calculateAttributeLocation(configuration, m_array_attribute, n_type); stream << "layout(location = " << location << ") "; } stream << "in " << type_name << " attribute_" << n_type << "[" << n_attributes << "];\n"; } stream << "\n"; } /* Varying declaration */ stream << "out int " << varying_name << ";\n\n"; /* Main */ stream << "void main()\n" "{\n"; for (GLint n_type = 0; n_type < n_types; ++n_type) { const GLint n_elements = configuration.m_n_elements[n_type]; const GLchar* type_name = configuration.m_type_names[n_type]; stream << "// " << type_name << "\n"; /* if (attribute_name != type(values)) * { * varying = 0; * } */ for (GLint attribute = 0; attribute < n_attributes; ++attribute) { const GLint attribute_ordinal = attribute + n_type * n_attributes; /* First attribute is verified with "if", rest with "else if" */ if (0 == attribute_ordinal) { stream << " if (attribute_"; } else { stream << " else if (attribute_"; } /* Spearated attributes are called: attribute_ORDINAL, arrayed: attribute_N_TYPE[INDEX] */ if (false == use_arrays) { stream << attribute_ordinal; } else { stream << n_type << "[" << attribute << "]"; } /* != type() */ stream << " != " << type_name << "("; /* Values for type constructor, depend on iteration */ switch (iteration) { case DOUBLE_DVEC2: /* Double attributes should be assigned the value: (n_attribute + gl_VertexID * 2) */ /* Dvec2 attribute components should be assigned the following vector values: (n_attribute + gl_VertexID * 3 + 1, n_attribute + gl_VertexID * 3 + 2)*/ if (1 == n_elements) { stream << attribute_ordinal << " + " << advancement_str << " * 2"; } else { stream << attribute_ordinal << " + " << advancement_str << " * 3 + 1" << ", " << attribute_ordinal << " + " << advancement_str << " * 3 + 2"; } break; case DVEC3_DVEC4: /* Dvec3 attribute components should be assigned the following vector values: (n_attribute + gl_VertexID * 3 + 0, n_attribute + gl_VertexID * 3 + 1, n_attribute + gl_VertexID * 3 + 2). Dvec4 attribute components should be assigned the following vector values: (n_attribute + gl_VertexID * 4 + 0, n_attribute + gl_VertexID * 4 + 1, n_attribute + gl_VertexID * 4 + 2, n_attribute + gl_VertexID * 4 + 3).*/ for (GLint element = 0; element < n_elements; ++element) { stream << attribute_ordinal << " + " << advancement_str << " * " << n_elements << " + " << element; if (n_elements != element + 1) { stream << ", "; } } break; case DMAT2: case DMAT3X2_DMAT4X2: case DMAT2X3_DMAT2X4: case DMAT3_DMAT3X4: case DMAT4X3_DMAT4: /* Subsequent matrix elements should be assigned the following value: (n_type + n_attribute + gl_VertexID * 16 + n_value)*/ for (GLint element = 0; element < n_elements; ++element) { stream << n_type << " + " << attribute_ordinal << " + " << advancement_str << " * 16 + " << element; if (n_elements != element + 1) { stream << ", "; } } break; } /* type() { varying = 0 } */ stream << "))\n" << " {\n" << " " << varying_name << " = 0;\n" << " }\n"; } } /* All attributes verified: else { varyin = 1 } Close main body */ stream << " else\n" << " {\n" << " " << varying_name << " = 1;\n" << " }\n" << "}\n\n"; /* Store results */ out_varying_name = varying_name; out_vertex_shader_code = stream.str(); } /** Get configuration of vertex array object * * @param iteration Iteration id * @param out_configuration Configuration **/ void LimitTest::getVertexArrayConfiguration(_iteration iteration, attributeConfiguration& out_configuration) const { static const GLuint n_elements_per_scalar = 1; static const GLuint n_elements_per_vec2 = 2; static const GLuint n_elements_per_vec3 = 3; static const GLuint n_elements_per_vec4 = 4; static const GLuint n_elements_per_mat2 = 4; static const GLuint n_elements_per_mat2x3 = 6; static const GLuint n_elements_per_mat2x4 = 8; static const GLuint n_elements_per_mat3 = 9; static const GLuint n_elements_per_mat3x2 = 6; static const GLuint n_elements_per_mat3x4 = 12; static const GLuint n_elements_per_mat4 = 16; static const GLuint n_elements_per_mat4x2 = 8; static const GLuint n_elements_per_mat4x3 = 12; const GLint max_vertex_attribs = GetMaxVertexAttribs(); switch (iteration) { case DOUBLE_DVEC2: { static const GLint n_elements[] = { n_elements_per_scalar, n_elements_per_vec2 }; static const GLint n_types = sizeof(n_elements) / sizeof(n_elements[0]); static const GLint divisor = 2; static const GLchar* type_names[] = { "double", "dvec2" }; static const GLint n_rows[] = { 1, 2, }; out_configuration.m_n_attributes_per_group = max_vertex_attribs / divisor; out_configuration.m_n_elements = n_elements; out_configuration.m_n_rows = n_rows; out_configuration.m_n_types = n_types; out_configuration.m_type_names = type_names; } break; case DVEC3_DVEC4: { static const GLint n_elements[] = { n_elements_per_vec3, n_elements_per_vec4 }; static const GLint n_types = sizeof(n_elements) / sizeof(n_elements[0]); static const GLint divisor = 4; static const GLchar* type_names[] = { "dvec3", "dvec4" }; static const GLint n_rows[] = { 3, 4, }; out_configuration.m_n_attributes_per_group = max_vertex_attribs / divisor; out_configuration.m_n_elements = n_elements; out_configuration.m_n_rows = n_rows; out_configuration.m_n_types = n_types; out_configuration.m_type_names = type_names; } break; case DMAT2: { static const GLint n_elements[] = { n_elements_per_mat2 }; static const GLint n_types = sizeof(n_elements) / sizeof(n_elements[0]); static const GLint divisor = 2; static const GLchar* type_names[] = { "dmat2" }; static const GLint n_rows[] = { 2 }; out_configuration.m_n_attributes_per_group = max_vertex_attribs / divisor; out_configuration.m_n_elements = n_elements; out_configuration.m_n_rows = n_rows; out_configuration.m_n_types = n_types; out_configuration.m_type_names = type_names; } break; case DMAT3X2_DMAT4X2: { static const GLint n_elements[] = { n_elements_per_mat3x2, n_elements_per_mat4x2 }; static const GLint n_types = sizeof(n_elements) / sizeof(n_elements[0]); static const GLint divisor = 8; static const GLchar* type_names[] = { "dmat3x2", "dmat4x2" }; static const GLint n_rows[] = { 2, 2 }; out_configuration.m_n_attributes_per_group = max_vertex_attribs / divisor; out_configuration.m_n_elements = n_elements; out_configuration.m_n_rows = n_rows; out_configuration.m_n_types = n_types; out_configuration.m_type_names = type_names; } break; case DMAT2X3_DMAT2X4: { static const GLint n_elements[] = { n_elements_per_mat2x3, n_elements_per_mat2x4 }; static const GLint n_types = sizeof(n_elements) / sizeof(n_elements[0]); static const GLint divisor = 8; static const GLchar* type_names[] = { "dmat2x3", "dmat2x4" }; static const GLint n_rows[] = { 3, 4 }; out_configuration.m_n_attributes_per_group = max_vertex_attribs / divisor; out_configuration.m_n_elements = n_elements; out_configuration.m_n_rows = n_rows; out_configuration.m_n_types = n_types; out_configuration.m_type_names = type_names; } break; case DMAT3_DMAT3X4: { static const GLint n_elements[] = { n_elements_per_mat3, n_elements_per_mat3x4 }; static const GLint n_types = sizeof(n_elements) / sizeof(n_elements[0]); static const GLint divisor = 12; static const GLchar* type_names[] = { "dmat3", "dmat3x4" }; static const GLint n_rows[] = { 3, 4 }; out_configuration.m_n_attributes_per_group = max_vertex_attribs / divisor; out_configuration.m_n_elements = n_elements; out_configuration.m_n_rows = n_rows; out_configuration.m_n_types = n_types; out_configuration.m_type_names = type_names; } break; case DMAT4X3_DMAT4: { static const GLint n_elements[] = { n_elements_per_mat4x3, n_elements_per_mat4 }; static const GLint n_types = sizeof(n_elements) / sizeof(n_elements[0]); static const GLint divisor = 16; static const GLchar* type_names[] = { "dmat4x3", "dmat4" }; static const GLint n_rows[] = { 3, 4 }; out_configuration.m_n_attributes_per_group = max_vertex_attribs / divisor; out_configuration.m_n_elements = n_elements; out_configuration.m_n_rows = n_rows; out_configuration.m_n_types = n_types; out_configuration.m_type_names = type_names; } break; } calculateVertexLength(out_configuration); } /** Logs iteration and configuration of test * * @param iteration Iteration id * @param use_arrays If attributes are grouped in arrays * @param use_locations If manual attribute locations are used * @param attribute_type Regular, constant or per instance **/ void LimitTest::logTestIterationAndConfig(_iteration iteration, _attributeType attribute_type, bool use_arrays, bool use_locations) const { tcu::MessageBuilder message = m_log << tcu::TestLog::Message; switch (iteration) { case DOUBLE_DVEC2: message << "Iteration: double + dvec2"; break; case DVEC3_DVEC4: message << "Iteration: devc3 + dvec4"; break; case DMAT2: message << "Iteration: dmat2"; break; case DMAT3X2_DMAT4X2: message << "Iteration: dmat3x2 + dmat4x2"; break; case DMAT2X3_DMAT2X4: message << "Iteration: dmat2x3 + dmat2x4"; break; case DMAT3_DMAT3X4: message << "Iteration: dmat3 + dmat3x4"; break; case DMAT4X3_DMAT4: message << "Iteration: dmat4x3 + dmat4"; break; } message << "Configuration: "; if (true == use_arrays) { message << "arrayed attributes"; } else { message << "separate attributes"; } message << ", "; if (true == use_locations) { message << "reversed locations"; } else { message << "default locations"; } message << ", "; switch (attribute_type) { case REGULAR: message << "vertex attribute divisor: 0"; break; case CONSTANT: message << "constant vertex attribute"; break; case PER_INSTANCE: message << "vertex attribute divisor: 1"; break; } message << tcu::TestLog::EndMessage; } /** Prepare program info for given configuration * * @param iteration Iteration id * @param use_arrays If attributes should be grouped in arrays * @param use_locations If manual attribute locations should be used * @param use_vertex_attrib_divisor If vertex attribute divisor should be used * @param program_info Program info **/ void LimitTest::prepareProgram(_iteration iteration, bool use_arrays, bool use_locations, bool use_vertex_attrib_divisor, programInfo& program_info) { static const GLchar* fragment_shader_code = "#version 400\n" "#extension GL_ARB_vertex_attrib_64bit : require\n" "\n" "precision highp float;\n" "\n" "void main()\n" "{\n" " discard;\n" "}\n\n"; std::string varying_name; std::string vertex_shader_code; program_info.m_program_id = gl.createProgram(); GLU_EXPECT_NO_ERROR(gl.getError(), "CreateProgram"); getProgramDetails(iteration, use_arrays, use_locations, use_vertex_attrib_divisor, varying_name, vertex_shader_code); { const GLchar* temp_varying_name = varying_name.c_str(); gl.transformFeedbackVaryings(program_info.m_program_id, m_n_varyings, &temp_varying_name, GL_INTERLEAVED_ATTRIBS); GLU_EXPECT_NO_ERROR(gl.getError(), "TransformFeedbackVaryings"); } BuildProgram(fragment_shader_code, program_info.m_program_id, vertex_shader_code.c_str(), program_info.m_fragment_shader_id, program_info.m_vertex_shader_id); } /** Configure vertex array object for all attributes * * @param iteration Iteration id * @param attribute_type Regular, constant or per instance * @param program_id Program object id * @param use_arrays If attributes are grouped with arrays **/ void LimitTest::prepareVertexArray(_iteration iteration, _attributeType attribute_type, GLuint program_id, bool use_arrays) const { const GLint max_vertex_attribs = GetMaxVertexAttribs(); const GLuint vertex_attrib_divisor = (PER_INSTANCE == attribute_type) ? 1 : 0; attributeConfiguration configuration; getVertexArrayConfiguration(iteration, configuration); /* Set vertex attributes divisor and disable */ for (GLint i = 0; i < max_vertex_attribs; ++i) { gl.vertexAttribDivisor(i, vertex_attrib_divisor); GLU_EXPECT_NO_ERROR(gl.getError(), "VertexAttribDivisor"); gl.disableVertexAttribArray(i); GLU_EXPECT_NO_ERROR(gl.getError(), "DisableVertexAttribArray"); } for (GLint n_type = 0; n_type < configuration.m_n_types; ++n_type) { configureAttribute(iteration, configuration, n_type, program_id, use_arrays, (CONSTANT != attribute_type)); } } /** Prepare vertex buffer data for given iteration * * @param iteration Iteration id **/ void LimitTest::prepareVertexArrayBuffer(_iteration iteration) { GLuint buffer_length = 0; attributeConfiguration configuration; getVertexArrayConfiguration(iteration, configuration); buffer_length = m_n_vertices * configuration.m_vertex_length; std::vector buffer_data; buffer_data.resize(buffer_length); for (GLuint vertex = 0; vertex < m_n_vertices; ++vertex) { setAttributes(iteration, configuration, vertex, buffer_data); } gl.bufferData(GL_ARRAY_BUFFER, buffer_length * sizeof(GLdouble), &buffer_data[0], GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR(gl.getError(), "BufferData"); } /** Set all attributes for * * @param iteration Iteration id * @param configuration Attribute configuration * @param vertex Vertex orinal number * @param out_buffer_data Buffer data **/ void LimitTest::setAttributes(_iteration iteration, const attributeConfiguration& configuration, GLuint vertex, std::vector& out_buffer_data) const { const GLint n_types = configuration.m_n_types; for (GLint n_type = 0; n_type < n_types; ++n_type) { switch (iteration) { case DOUBLE_DVEC2: setAttributes_a(configuration, n_type, vertex, out_buffer_data); break; case DVEC3_DVEC4: setAttributes_b(configuration, n_type, vertex, out_buffer_data); break; case DMAT2: case DMAT3X2_DMAT4X2: case DMAT2X3_DMAT2X4: case DMAT3_DMAT3X4: case DMAT4X3_DMAT4: setAttributes_c(configuration, n_type, vertex, out_buffer_data); break; } } } /** Set attributes of given for , as described in "iteration a". * * @param configuration Attribute configuration * @param n_type "n_type" ordinal number * @param vertex Vertex orinal number * @param out_buffer_data Buffer data **/ void LimitTest::setAttributes_a(const attributeConfiguration& configuration, GLint n_type, GLuint vertex, std::vector& out_buffer_data) const { const GLint n_elements = configuration.m_n_elements[n_type]; if (1 == n_elements) { setAttributes_a_scalar(configuration, n_type, vertex, out_buffer_data); } else { setAttributes_a_vec(configuration, n_type, vertex, out_buffer_data); } } /** Set scalar double attributes of given for , as described in "iteration a". * * @param configuration Attribute configuration * @param n_type "n_type" ordinal number * @param vertex Vertex orinal number * @param out_buffer_data Buffer data **/ void LimitTest::setAttributes_a_scalar(const attributeConfiguration& configuration, GLint n_type, GLuint vertex, std::vector& out_buffer_data) const { const GLint n_attributes = configuration.m_n_attributes_per_group; const GLint attribute_index = n_attributes * n_type; GLuint vertex_offset = vertex * configuration.m_vertex_length; const GLint group_offset = calculateAttributeGroupOffset(configuration, n_type) + vertex_offset; /* Double attributes should be assigned the value: (n_attribute + gl_VertexID * 2) */ for (GLint attribute = 0; attribute < n_attributes; ++attribute) { const GLuint attribute_offset = attribute + group_offset; out_buffer_data[attribute_offset] = attribute + attribute_index + vertex * 2; } } /** Set dvec2 attributes of given for , as described in "iteration a". * * @param configuration Attribute configuration * @param n_type "n_type" ordinal number * @param vertex Vertex orinal number * @param out_buffer_data Buffer data **/ void LimitTest::setAttributes_a_vec(const attributeConfiguration& configuration, GLint n_type, GLuint vertex, std::vector& out_buffer_data) const { const GLint n_attributes = configuration.m_n_attributes_per_group; const GLint attribute_index = n_attributes * n_type; const GLint n_elements = configuration.m_n_elements[n_type]; GLuint vertex_offset = vertex * configuration.m_vertex_length; const GLint group_offset = calculateAttributeGroupOffset(configuration, n_type) + vertex_offset; /* Dvec2 attribute components should be assigned the following vector values: (n_attribute + gl_VertexID * 3 + 1, n_attribute + gl_VertexID * 3 + 2)*/ for (GLint attribute = 0; attribute < n_attributes; ++attribute) { const GLuint attribute_offset = n_elements * attribute + group_offset; for (GLint i = 0; i < n_elements; ++i) { out_buffer_data[attribute_offset + i] = attribute + attribute_index + vertex * 3 + i + 1; } } } /** Set attributes of given for , as described in "iteration b". * * @param configuration Attribute configuration * @param n_type "n_type" ordinal number * @param vertex Vertex orinal number * @param out_buffer_data Buffer data **/ void LimitTest::setAttributes_b(const attributeConfiguration& configuration, GLint n_type, GLuint vertex, std::vector& out_buffer_data) const { const GLint n_attributes = configuration.m_n_attributes_per_group; const GLint attribute_index = n_attributes * n_type; const GLint n_elements = configuration.m_n_elements[n_type]; GLuint vertex_offset = vertex * configuration.m_vertex_length; const GLint group_offset = calculateAttributeGroupOffset(configuration, n_type) + vertex_offset; /* Dvec3 attribute components should be assigned the following vector values: (n_attribute + gl_VertexID * 3 + 0, n_attribute + gl_VertexID * 3 + 1, n_attribute + gl_VertexID * 3 + 2). Dvec4 attribute components should be assigned the following vector values: (n_attribute + gl_VertexID * 4 + 0, n_attribute + gl_VertexID * 4 + 1, n_attribute + gl_VertexID * 4 + 2, n_attribute + gl_VertexID * 4 + 3).*/ for (GLint attribute = 0; attribute < n_attributes; ++attribute) { const GLuint attribute_offset = n_elements * attribute + group_offset; for (GLint i = 0; i < n_elements; ++i) { out_buffer_data[attribute_offset + i] = attribute + attribute_index + vertex * n_elements + i; } } } /** Set attributes of given for , as described in "iteration c". * * @param configuration Attribute configuration * @param n_type "n_type" ordinal number * @param vertex Vertex orinal number * @param out_buffer_data Buffer data **/ void LimitTest::setAttributes_c(const attributeConfiguration& configuration, GLint n_type, GLuint vertex, std::vector& out_buffer_data) const { const GLint n_attributes = configuration.m_n_attributes_per_group; const GLint attribute_index = n_attributes * n_type; const GLint n_elements = configuration.m_n_elements[n_type]; GLuint vertex_offset = vertex * configuration.m_vertex_length; const GLint group_offset = calculateAttributeGroupOffset(configuration, n_type) + vertex_offset; /* Subsequent matrix elements should be assigned the following value: (n_type + n_attribute + gl_VertexID * 16 + n_value)*/ for (GLint attribute = 0; attribute < n_attributes; ++attribute) { const GLuint attribute_offset = n_elements * attribute + group_offset; for (GLint i = 0; i < n_elements; ++i) { out_buffer_data[attribute_offset + i] = n_type + attribute + attribute_index + vertex * 16 + i; } } } /** Run test with DrawArrays routine * * @return true if test pass, false otherwise **/ bool LimitTest::testDrawArrays() const { gl.beginTransformFeedback(GL_POINTS); GLU_EXPECT_NO_ERROR(gl.getError(), "BeginTransformFeedback"); gl.drawArrays(GL_POINTS, 0 /* first */, m_n_vertices); GLU_EXPECT_NO_ERROR(gl.getError(), "DrawArrays"); gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "EndTransformFeedback"); if (true == verifyResult(false)) { return true; } else { m_log << tcu::TestLog::Message << "Draw function: DrawArrays" << tcu::TestLog::EndMessage; return false; } } /** Run test with DrawArraysInstanced routine * * @return true if test pass, false otherwise **/ bool LimitTest::testDrawArraysInstanced() const { gl.beginTransformFeedback(GL_POINTS); GLU_EXPECT_NO_ERROR(gl.getError(), "BeginTransformFeedback"); gl.drawArraysInstanced(GL_POINTS, 0 /* first */, m_n_vertices, m_n_instances); GLU_EXPECT_NO_ERROR(gl.getError(), "DrawArraysInstanced"); gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "EndTransformFeedback"); if (true == verifyResult(true)) { return true; } else { m_log << tcu::TestLog::Message << "Draw function: DrawArraysInstanced" << tcu::TestLog::EndMessage; return false; } } /** Run test with DrawElements routine * * @return true if test pass, false otherwise **/ bool LimitTest::testDrawElements() const { gl.beginTransformFeedback(GL_POINTS); GLU_EXPECT_NO_ERROR(gl.getError(), "BeginTransformFeedback"); gl.drawElements(GL_POINTS, m_n_vertices, GL_UNSIGNED_INT, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "DrawElements"); gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "EndTransformFeedback"); if (true == verifyResult(false)) { return true; } else { m_log << tcu::TestLog::Message << "Draw function: DrawElements" << tcu::TestLog::EndMessage; return false; } } /** Run test with DrawElementsInstanced routine * * @return true if test pass, false otherwise **/ bool LimitTest::testDrawElementsInstanced() const { gl.beginTransformFeedback(GL_POINTS); GLU_EXPECT_NO_ERROR(gl.getError(), "BeginTransformFeedback"); gl.drawElementsInstanced(GL_POINTS, m_n_vertices, GL_UNSIGNED_INT, 0, m_n_instances); GLU_EXPECT_NO_ERROR(gl.getError(), "DrawElementsInstanced"); gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "EndTransformFeedback"); if (true == verifyResult(true)) { return true; } else { m_log << tcu::TestLog::Message << "Draw function: DrawElementsInstanced" << tcu::TestLog::EndMessage; return false; } } /** Test initialisation * **/ void LimitTest::testInit() { /* Prepare data for element array buffer */ std::vector indices_data; indices_data.resize(m_n_vertices); for (GLuint i = 0; i < m_n_vertices; ++i) { indices_data[i] = i; } /* Prepare vertex array object */ gl.genVertexArrays(1, &m_vertex_array_object_id); GLU_EXPECT_NO_ERROR(gl.getError(), "GenVertexArrays"); gl.bindVertexArray(m_vertex_array_object_id); GLU_EXPECT_NO_ERROR(gl.getError(), "BindVertexArray"); /* Generate buffers */ gl.genBuffers(1, &m_element_array_buffer_id); gl.genBuffers(1, &m_transoform_feedback_buffer_id); gl.genBuffers(1, &m_vertex_array_buffer_id); GLU_EXPECT_NO_ERROR(gl.getError(), "GenBuffers"); /* Prepare element array buffer */ gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_element_array_buffer_id); GLU_EXPECT_NO_ERROR(gl.getError(), "BindBuffer"); gl.bufferData(GL_ELEMENT_ARRAY_BUFFER, m_n_vertices * sizeof(GLuint), &indices_data[0], GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR(gl.getError(), "BufferData"); /* Prepare transform feedback buffer */ gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_transoform_feedback_buffer_id); GLU_EXPECT_NO_ERROR(gl.getError(), "BindBuffer"); gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, m_transform_feedback_buffer_size, 0 /* data */, GL_DYNAMIC_COPY); GLU_EXPECT_NO_ERROR(gl.getError(), "BufferData"); /* Bind array buffer for future use */ gl.bindBuffer(GL_ARRAY_BUFFER, m_vertex_array_buffer_id); GLU_EXPECT_NO_ERROR(gl.getError(), "BindBuffer"); /* Disabe rasterization */ gl.enable(GL_RASTERIZER_DISCARD); GLU_EXPECT_NO_ERROR(gl.getError(), "Enable"); } /** Tests specified "iteration" * * @param iteration Iteration id * * @return true if tests pass, false otherwise **/ bool LimitTest::testIteration(_iteration iteration) { bool result = true; /* Program infos */ programInfo _no_array__no_location______regular(gl); programInfo use_array__no_location______regular(gl); programInfo _no_array_use_location______regular(gl); programInfo use_array_use_location______regular(gl); programInfo _no_array__no_location_per_instance(gl); programInfo use_array__no_location_per_instance(gl); programInfo _no_array_use_location_per_instance(gl); programInfo use_array_use_location_per_instance(gl); /* Prepare programs for all configuration */ prepareProgram(iteration, false, false, false, _no_array__no_location______regular); prepareProgram(iteration, true, false, false, use_array__no_location______regular); prepareProgram(iteration, false, true, false, _no_array_use_location______regular); prepareProgram(iteration, true, true, false, use_array_use_location______regular); prepareProgram(iteration, false, false, true, _no_array__no_location_per_instance); prepareProgram(iteration, true, false, true, use_array__no_location_per_instance); prepareProgram(iteration, false, true, true, _no_array_use_location_per_instance); prepareProgram(iteration, true, true, true, use_array_use_location_per_instance); /* Bind buffers */ gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_element_array_buffer_id); gl.bindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_transoform_feedback_buffer_id, 0, m_transform_feedback_buffer_size); GLU_EXPECT_NO_ERROR(gl.getError(), "BindBufferRange"); /* Prepare vertex array buffer for iteration */ prepareVertexArrayBuffer(iteration); /* Regular and instanced draw calls, vertex attribute divisor: 0 */ if (false == testProgram(iteration, _no_array__no_location______regular.m_program_id, false)) { logTestIterationAndConfig(iteration, REGULAR, false /* use_arrays */, false /* use_locations */); result = false; } if (false == testProgram(iteration, use_array__no_location______regular.m_program_id, true)) { logTestIterationAndConfig(iteration, REGULAR, true /* use_arrays */, false /* use_locations */); result = false; } if (false == testProgram(iteration, _no_array_use_location______regular.m_program_id, false)) { logTestIterationAndConfig(iteration, REGULAR, false /* use_arrays */, true /* use_locations */); result = false; } if (false == testProgram(iteration, use_array_use_location______regular.m_program_id, true)) { logTestIterationAndConfig(iteration, REGULAR, true /* use_arrays */, true /* use_locations */); result = false; } /* Regular draw calls, constant vertex attribute */ if (false == testProgramWithConstant(iteration, _no_array__no_location_per_instance.m_program_id, false)) { logTestIterationAndConfig(iteration, CONSTANT, false /* use_arrays */, false /* use_locations */); result = false; } if (false == testProgramWithConstant(iteration, use_array__no_location_per_instance.m_program_id, true)) { logTestIterationAndConfig(iteration, CONSTANT, true /* use_arrays */, false /* use_locations */); result = false; } if (false == testProgramWithConstant(iteration, _no_array_use_location_per_instance.m_program_id, false)) { logTestIterationAndConfig(iteration, CONSTANT, false /* use_arrays */, true /* use_locations */); result = false; } if (false == testProgramWithConstant(iteration, use_array_use_location_per_instance.m_program_id, true)) { logTestIterationAndConfig(iteration, CONSTANT, true /* use_arrays */, true /* use_locations */); result = false; } /* Instanced draw calls, vertex attribute divisor: 1 */ if (false == testProgramWithDivisor(iteration, _no_array__no_location_per_instance.m_program_id, false)) { logTestIterationAndConfig(iteration, PER_INSTANCE, false /* use_arrays */, false /* use_locations */); result = false; } if (false == testProgramWithDivisor(iteration, use_array__no_location_per_instance.m_program_id, true)) { logTestIterationAndConfig(iteration, PER_INSTANCE, true /* use_arrays */, false /* use_locations */); result = false; } if (false == testProgramWithDivisor(iteration, _no_array_use_location_per_instance.m_program_id, false)) { logTestIterationAndConfig(iteration, PER_INSTANCE, false /* use_arrays */, true /* use_locations */); result = false; } if (false == testProgramWithDivisor(iteration, use_array_use_location_per_instance.m_program_id, true)) { logTestIterationAndConfig(iteration, PER_INSTANCE, true /* use_arrays */, true /* use_locations */); result = false; } /* Done */ return result; } /** Tests regular and instanced draw calls with vertex attribute divisor set to 0 * * @param iteration Iteration id * @param program_id Program object id * @param use_arrays true if arrays of attributes are used * * @return true if tests pass, false otherwise **/ bool LimitTest::testProgram(_iteration iteration, GLuint program_id, bool use_arrays) const { bool result = true; gl.useProgram(program_id); GLU_EXPECT_NO_ERROR(gl.getError(), "UseProgram"); prepareVertexArray(iteration, REGULAR, program_id, use_arrays); if (false == testDrawArrays()) { result = false; } if (false == testDrawElements()) { result = false; } if (false == testDrawArraysInstanced()) { result = false; } if (false == testDrawElementsInstanced()) { result = false; } return result; } /** Tests constant attributes value, set with VertexAttribLd* routines * * @param iteration Iteration id * @param program_id Program object id * @param use_arrays true if arrays of attributes are used * * @return true if tests pass, false otherwise **/ bool LimitTest::testProgramWithConstant(_iteration iteration, GLuint program_id, bool use_arrays) const { bool result = true; gl.useProgram(program_id); GLU_EXPECT_NO_ERROR(gl.getError(), "UseProgram"); prepareVertexArray(iteration, CONSTANT, program_id, use_arrays); if (false == testDrawArrays()) { result = false; } if (false == testDrawElements()) { result = false; } return result; } /** Tests instanced draw calls with vertex attribute divisor set to 1 * * @param iteration Iteration id * @param program_id Program object id * @param use_arrays true if arrays of attributes are used * * @return true if tests pass, false otherwise **/ bool LimitTest::testProgramWithDivisor(_iteration iteration, GLuint program_id, bool use_arrays) const { bool result = true; gl.useProgram(program_id); GLU_EXPECT_NO_ERROR(gl.getError(), "UseProgram"); prepareVertexArray(iteration, PER_INSTANCE, program_id, use_arrays); if (false == testDrawArraysInstanced()) { result = false; } if (false == testDrawElementsInstanced()) { result = false; } return result; } /** Verifies results * * @param use_instancing true if instanced draw call was made, otherwise false * * @result true if all vertices outputed 1, false otherwise **/ bool LimitTest::verifyResult(bool use_instancing) const { _varyingType* buffer_data = (_varyingType*)gl.mapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, GL_READ_ONLY); const GLuint n_instances = (true == use_instancing) ? m_n_instances : 1; bool result = true; GLU_EXPECT_NO_ERROR(gl.getError(), "MapBuffer"); if (0 == buffer_data) { TCU_FAIL("Failed to map GL_TRANSFORM_FEEDBACK_BUFFER buffer"); } /* For each instance */ for (GLuint instance = 0; instance < n_instances; ++instance) { const GLuint instance_offset = instance * m_n_vertices * m_n_varyings; /* For each vertex */ for (GLuint vertex = 0; vertex < m_n_vertices; ++vertex) { const GLuint vertex_offset = vertex * m_n_varyings; if (1 != buffer_data[vertex_offset + instance_offset]) { if (true == use_instancing) { m_log << tcu::TestLog::Message << "Failure. Instance: " << instance << " Vertex: " << vertex << tcu::TestLog::EndMessage; } else { m_log << tcu::TestLog::Message << "Failure. Vertex: " << vertex << tcu::TestLog::EndMessage; } /* Save failure and break loop */ result = false; /* Sorry about that, but this is nested loop */ goto end; } } } end: gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER); GLU_EXPECT_NO_ERROR(gl.getError(), "UnmapBuffer"); return result; } /** Implementation of conformance test "4", description follows. * * Make sure non-trivial VAO configurations are correctly supported * for double-precision floating-point types. * * Consider the following Vertex Buffer Object configurations: * * BO1: * 0 72 73 75 91 96 * --------------+-+--+-------+--- * | A |B| C| D | E| (times 1024) * ------------------------------- * * where: * * A: 3x3 double matrix (72 bytes) * B: 1 unsigned byte (1 byte) * C: 1 short (2 bytes) * D: 2 doubles (16 bytes) * E: padding (5 bytes) * (+) -------- * 96 bytes * * BO2: * --+------------------ * |A| B | (times 1024) * --+------------------ * * where: * * A: 1 signed byte (1 byte) * B: 4x2 double matrix (64 bytes) * (+) -------- * 65 bytes * * A VAO used for the test should be configured as described * below: * * Att 0 (L): VAP-type:GL_DOUBLE, GLSL-type: dmat3, stride:96, * offset: 0, normalized:0, source:BO1; * Att 1 (F): VAP-type:GL_UNSIGNED_BYTE, GLSL-type: float, stride:5, * offset: 0, normalized:1, source:BO2; * Att 2 (L): VAP-type:GL_DOUBLE, GLSL-type: dvec2, stride:96, * offset: 75, normalized:0, source:BO1; * Att 3 (L): VAP-type:GL_DOUBLE, GLSL-type: double, stride:48, * offset: 0, normalized:0, source:BO1; * Att 4 (L): VAP-type:GL_DOUBLE, GLSL-type: dmat4x2, stride:65, * offset: 1, normalized:0, source:BO2; * Att 5 (F): VAP-type:GL_SHORT, GLSL-type: float, stride:96, * offset: 73, normalized:0, source:BO1; * Att 6 (I): VAP-type:GL_BYTE, GLSL-type: int, stride:96, * offset: 72, normalized:1, source:BO1; * * where: * * GLSL-type: Input variable type, as to be used in corresponding * vertex shader. * (F): glVertexAttribPointer() call should be used to configure * given vertex attribute array; * (I): glVertexAttribIPointer() call should be used to configure * given vertex attribute array; * (L): glVertexAttribLPointer() call should be used to configure * given vertex attribute array; * VAP-type: argument as passed to corresponding * glVertexAttrib*Pointer() call. * * The test should use a program object consisting only of VS. * The shader should read all the attributes and store the * values in corresponding output variables. These should then be * XFBed out to the test implementation, which should then verify * the values read in the shader are valid in light of the specification. * * All the draw call types described in test 3) should be tested. * A single draw call for each of the types, rendering a total of * 1024 points should be used for the purpose of the test * **/ class VAOTest : public Base { public: /* Public methods */ VAOTest(deqp::Context& context); virtual ~VAOTest() { } /* Public methods inheritated from TestCase */ virtual void deinit(); virtual tcu::TestNode::IterateResult iterate(); private: /* Private type declarations */ enum _draw_call_type { DRAW_CALL_TYPE_ARRAYS, DRAW_CALL_TYPE_ELEMENTS, /* Always last */ DRAW_CALL_TYPE_COUNT }; /* Private methods */ bool executeTest(_draw_call_type draw_call, bool instanced, bool zero_vertex_attrib_divisor); void initBufferObjects(); void initBuffers(); void initProgramObject(); void initVAO(); bool verifyXFBData(const void* data, _draw_call_type draw_call, bool instanced, bool zero_vertex_attrib_divisor); /* Private fields */ unsigned char* m_bo_1_data; unsigned int m_bo_1_data_size; unsigned int m_bo_1_offset_matrix; unsigned int m_bo_1_offset_ubyte; unsigned int m_bo_1_offset_short; unsigned int m_bo_1_offset_double; unsigned char* m_bo_2_data; unsigned int m_bo_2_data_size; unsigned int m_bo_2_offset_sbyte; unsigned int m_bo_2_offset_matrix; unsigned short* m_bo_index_data; unsigned int m_bo_index_data_size; glw::GLuint m_bo_id_1; glw::GLuint m_bo_id_2; glw::GLuint m_bo_id_indices; glw::GLuint m_bo_id_result; glw::GLint m_po_bo1_dmat3_attr_location; glw::GLint m_po_bo2_dmat4x2_attr_location; glw::GLint m_po_bo1_double_attr_location; glw::GLint m_po_bo1_dvec2_attr_location; glw::GLint m_po_bo1_float2_attr_location; glw::GLint m_po_bo1_int_attr_location; glw::GLint m_po_bo2_float_attr_location; glw::GLuint m_po_id; glw::GLuint m_vao_id; glw::GLuint m_vs_id; unsigned int m_xfb_bo1_dmat3_offset; unsigned int m_xfb_bo1_dmat3_size; unsigned int m_xfb_bo1_double_offset; unsigned int m_xfb_bo1_double_size; unsigned int m_xfb_bo1_dvec2_offset; unsigned int m_xfb_bo1_dvec2_size; unsigned int m_xfb_bo1_float2_offset; unsigned int m_xfb_bo1_float2_size; unsigned int m_xfb_bo1_int_offset; unsigned int m_xfb_bo1_int_size; unsigned int m_xfb_bo2_dmat4x2_offset; unsigned int m_xfb_bo2_dmat4x2_size; unsigned int m_xfb_bo2_float_offset; unsigned int m_xfb_bo2_float_size; unsigned int m_xfb_total_size; const unsigned int m_bo_1_batch_size; const unsigned int m_bo_2_batch_size; const unsigned int m_n_batches; const unsigned int m_n_draw_call_instances; const unsigned int m_nonzero_vertex_attrib_divisor; const unsigned int m_po_bo1_dmat3_attr_offset; const unsigned int m_po_bo1_dmat3_attr_stride; const unsigned int m_po_bo1_double_attr_offset; const unsigned int m_po_bo1_double_attr_stride; const unsigned int m_po_bo1_dvec2_attr_offset; const unsigned int m_po_bo1_dvec2_attr_stride; const unsigned int m_po_bo1_float2_attr_offset; const unsigned int m_po_bo1_float2_attr_stride; const unsigned int m_po_bo1_int_attr_offset; const unsigned int m_po_bo1_int_attr_stride; const unsigned int m_po_bo2_dmat4x2_attr_offset; const unsigned int m_po_bo2_dmat4x2_attr_stride; const unsigned int m_po_bo2_float_attr_offset; const unsigned int m_po_bo2_float_attr_stride; }; /** Constructor * * @param context CTS context instance **/ VAOTest::VAOTest(deqp::Context& context) : Base(context, "vao", "Verify that non-trivial VAO configurations are correctly supported " "for double-precision floating-point types.") , m_bo_1_data(DE_NULL) , m_bo_1_data_size(0) , m_bo_1_offset_matrix(0) , m_bo_1_offset_ubyte(72) , m_bo_1_offset_short(73) , m_bo_1_offset_double(75) , m_bo_2_data(DE_NULL) , m_bo_2_data_size(0) , m_bo_2_offset_sbyte(0) , m_bo_2_offset_matrix(1) , m_bo_index_data(DE_NULL) , m_bo_index_data_size(0) , m_bo_id_1(0) , m_bo_id_2(0) , m_bo_id_indices(0) , m_bo_id_result(0) , m_po_bo1_dmat3_attr_location(-1) , m_po_bo2_dmat4x2_attr_location(-1) , m_po_bo1_double_attr_location(-1) , m_po_bo1_dvec2_attr_location(-1) , m_po_bo1_float2_attr_location(-1) , m_po_bo1_int_attr_location(-1) , m_po_bo2_float_attr_location(-1) , m_po_id(0) , m_vao_id(0) , m_vs_id(0) , m_xfb_bo1_dmat3_offset(0) , m_xfb_bo1_dmat3_size(0) , m_xfb_bo1_double_offset(0) , m_xfb_bo1_double_size(0) , m_xfb_bo1_dvec2_offset(0) , m_xfb_bo1_dvec2_size(0) , m_xfb_bo1_float2_offset(0) , m_xfb_bo1_float2_size(0) , m_xfb_bo1_int_offset(0) , m_xfb_bo1_int_size(0) , m_xfb_bo2_dmat4x2_offset(0) , m_xfb_bo2_dmat4x2_size(0) , m_xfb_bo2_float_offset(0) , m_xfb_bo2_float_size(0) , m_xfb_total_size(0) , m_bo_1_batch_size(96) , m_bo_2_batch_size(65) , m_n_batches(1024) , m_n_draw_call_instances(4) , m_nonzero_vertex_attrib_divisor(2) , m_po_bo1_dmat3_attr_offset(0) , m_po_bo1_dmat3_attr_stride(96) , m_po_bo1_double_attr_offset(0) , m_po_bo1_double_attr_stride(48) , m_po_bo1_dvec2_attr_offset(75) , m_po_bo1_dvec2_attr_stride(96) , m_po_bo1_float2_attr_offset(73) , m_po_bo1_float2_attr_stride(96) , m_po_bo1_int_attr_offset(72) , m_po_bo1_int_attr_stride(96) , m_po_bo2_dmat4x2_attr_offset(1) , m_po_bo2_dmat4x2_attr_stride(65) , m_po_bo2_float_attr_offset(0) , m_po_bo2_float_attr_stride(5) { /* Nothing to be done here */ } /** Deinitializes GL objects and deallocates buffers that may have * been created during test execution */ void VAOTest::deinit() { if (m_bo_1_data != DE_NULL) { delete[] m_bo_1_data; m_bo_1_data = DE_NULL; } if (m_bo_2_data != DE_NULL) { delete[] m_bo_2_data; m_bo_2_data = DE_NULL; } if (m_bo_index_data != DE_NULL) { delete[] m_bo_index_data; m_bo_index_data = DE_NULL; } if (m_bo_id_1 != 0) { gl.deleteBuffers(1, &m_bo_id_1); m_bo_id_1 = 0; } if (m_bo_id_2 != 0) { gl.deleteBuffers(1, &m_bo_id_2); m_bo_id_2 = 0; } if (m_bo_id_indices != 0) { gl.deleteBuffers(1, &m_bo_id_indices); m_bo_id_indices = 0; } if (m_bo_id_result != 0) { gl.deleteBuffers(1, &m_bo_id_result); m_bo_id_result = 0; } if (m_po_id != 0) { gl.deleteProgram(m_po_id); m_po_id = 0; } if (m_vao_id != 0) { gl.deleteVertexArrays(1, &m_vao_id); m_vao_id = 0; } if (m_vs_id != 0) { gl.deleteShader(m_vs_id); m_vs_id = 0; } } /** Executes a single test iteration. * * This function may throw error exceptions if GL implementation misbehaves. * * @param draw_call Type of the draw call that should be issued. * @param instanced True if the draw call should be instanced, false otherwise. * @param zero_vertex_attrib_divisor True if a zero divisor should be used for all checked attributes, * false to use a value of m_nonzero_vertex_attrib_divisor as the divisor. * * @return true if the test iteration passed, false otherwise. **/ bool VAOTest::executeTest(_draw_call_type draw_call, bool instanced, bool zero_vertex_attrib_divisor) { bool result = true; gl.beginTransformFeedback(GL_POINTS); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback() call failed."); { const glw::GLint divisor = (zero_vertex_attrib_divisor) ? 0 : m_nonzero_vertex_attrib_divisor; const glw::GLint attributes[] = { m_po_bo1_dmat3_attr_location, m_po_bo1_dmat3_attr_location + 1, m_po_bo1_dmat3_attr_location + 2, m_po_bo2_dmat4x2_attr_location, m_po_bo2_dmat4x2_attr_location + 1, m_po_bo2_dmat4x2_attr_location + 2, m_po_bo2_dmat4x2_attr_location + 3, m_po_bo1_double_attr_location, m_po_bo1_dvec2_attr_location, m_po_bo1_float2_attr_location, m_po_bo1_int_attr_location, m_po_bo2_float_attr_location }; const unsigned int n_attributes = sizeof(attributes) / sizeof(attributes[0]); for (unsigned int n_attribute = 0; n_attribute < n_attributes; ++n_attribute) { glw::GLint attribute = attributes[n_attribute]; /* Configure vertex attribute divisor */ gl.vertexAttribDivisor(attribute, divisor); GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribDivisor() call failed."); } /* for (all attribute locations) */ /* Issue the draw call */ switch (draw_call) { case DRAW_CALL_TYPE_ARRAYS: { if (instanced) { gl.drawArraysInstanced(GL_POINTS, 0 /* first */, m_n_batches, m_n_draw_call_instances); GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArraysInstanced() call failed"); } else { gl.drawArrays(GL_POINTS, 0 /* first */, m_n_batches); GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays() call failed."); } break; } /* case DRAW_CALL_TYPE_ARRAYS: */ case DRAW_CALL_TYPE_ELEMENTS: { if (instanced) { gl.drawElementsInstanced(GL_POINTS, m_n_batches, GL_UNSIGNED_SHORT, DE_NULL /* indices */, m_n_draw_call_instances); GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawElementsInstanced() call failed."); } else { gl.drawElements(GL_POINTS, m_n_batches, GL_UNSIGNED_SHORT, DE_NULL); /* indices */ GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawElements() call failed."); } break; } /* case DRAW_CALL_TYPE_ELEMENTS: */ default: { TCU_FAIL("Unrecognized draw call type"); } } /* switch (draw_call) */ } gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback() call failed."); /* Retrieve the results */ const void* pXFBData = gl.mapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, GL_READ_ONLY); GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBuffer() call failed."); /* Verify the data */ result = verifyXFBData(pXFBData, draw_call, instanced, zero_vertex_attrib_divisor); /* Unmap the buffer */ gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER); GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer() call failed."); return result; } /** Initializes buffer objects that will be used by the test. * * This function may throw error exceptions if GL implementation misbehaves. **/ void VAOTest::initBufferObjects() { DE_ASSERT(m_bo_1_data != DE_NULL); DE_ASSERT(m_bo_2_data != DE_NULL); /* Generate BOs */ gl.genBuffers(1, &m_bo_id_1); gl.genBuffers(1, &m_bo_id_2); gl.genBuffers(1, &m_bo_id_indices); gl.genBuffers(1, &m_bo_id_result); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers() call(s) failed."); /* Initiailize BO storage */ gl.bindBuffer(GL_ARRAY_BUFFER, m_bo_id_1); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call failed."); gl.bufferData(GL_ARRAY_BUFFER, m_bo_1_data_size, m_bo_1_data, GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() call failed."); gl.bindBuffer(GL_ARRAY_BUFFER, m_bo_id_2); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call failed."); gl.bufferData(GL_ARRAY_BUFFER, m_bo_2_data_size, m_bo_2_data, GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() call failed."); gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_bo_id_indices); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call failed."); gl.bufferData(GL_ELEMENT_ARRAY_BUFFER, m_bo_index_data_size, m_bo_index_data, GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() call failed."); /* Finally, reserve sufficient amount of space for the data to be XFBed out from * the test program. We need: * * a) dmat3: (3 * 3 * 2) components: 18 float components * b) float: (1) component : 1 float component * c) dvec2: (2 * 2) components: 4 float components * d) double: (1 * 2) components: 2 float components * e) dmat4x2: (4 * 2 * 2) components: 16 float components * f) int: (1) components: 1 int component * g) float: (1) component: 1 float components * h) padding: 4 bytes because fp64 buffer needs 8 bytes alignment * (+)------ * (42 float + 1 int + 4 bytes padding) components times 1024 batches: 43008 floats, 1024 ints * * Don't forget about instanced draw calls. We'll be XFBing data for either 1 or m_n_draw_call_instances * instances. */ const unsigned int xfb_dat_pad = sizeof(int); const unsigned int xfb_data_size = static_cast((42 * sizeof(float) + sizeof(int) + xfb_dat_pad) * 1024 * m_n_draw_call_instances); gl.bindBuffer(GL_ARRAY_BUFFER, m_bo_id_result); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call failed."); gl.bufferData(GL_ARRAY_BUFFER, xfb_data_size, DE_NULL /* data */, GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() call failed."); /* Set up XFB bindings */ gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_bo_id_result); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call failed."); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* index */, m_bo_id_result); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase() call failed."); } /** Initializes buffers that will later be used to fill storage of buffer objects used by the test. */ void VAOTest::initBuffers() { DE_ASSERT(m_bo_1_data == DE_NULL); DE_ASSERT(m_bo_2_data == DE_NULL); DE_ASSERT(m_bo_index_data == DE_NULL); /* Prepare buffers storing underlying data. The buffers will be used for: * * - storage purposes; * - verification of the data XFBed from the vertex shader. */ m_bo_1_data_size = m_bo_1_batch_size * m_n_batches; m_bo_2_data_size = m_bo_2_batch_size * m_n_batches; m_bo_index_data_size = static_cast(sizeof(unsigned short) * m_n_batches); m_bo_1_data = new unsigned char[m_bo_1_data_size]; m_bo_2_data = new unsigned char[m_bo_2_data_size]; m_bo_index_data = new unsigned short[m_bo_index_data_size / sizeof(unsigned short)]; /* Workaround for alignment issue that may result in bus error on some platforms */ union { double d; unsigned char c[sizeof(double)]; } u; /* Fill index data */ for (unsigned short n_index = 0; n_index < (unsigned short)m_n_batches; ++n_index) { m_bo_index_data[n_index] = (unsigned short)((unsigned short)(m_n_batches - 1) - n_index); } /* Fill 3x3 matrix data in BO1 */ for (unsigned int n_matrix = 0; n_matrix < m_n_batches; ++n_matrix) { double* matrix_ptr = (double*)(m_bo_1_data + n_matrix * m_bo_1_batch_size + m_bo_1_offset_matrix); for (unsigned int n_element = 0; n_element < 9 /* 3x3 matrix */; ++n_element) { matrix_ptr[n_element] = (double)(n_matrix * 3 * 3 + n_element + 1); } } /* for (all matrices) */ /* Fill unsigned byte data in BO1 */ for (unsigned int n_element = 0; n_element < m_n_batches; ++n_element) { unsigned char* data_ptr = m_bo_1_data + n_element * m_bo_1_batch_size + m_bo_1_offset_ubyte; *data_ptr = (unsigned char)n_element; } /* Fill short data in BO1 */ for (unsigned int n_element = 0; n_element < m_n_batches; ++n_element) { unsigned short* data_ptr = (unsigned short*)(m_bo_1_data + n_element * m_bo_1_batch_size + m_bo_1_offset_short); *data_ptr = (unsigned short)n_element; } /* Fill 2 doubles data in BO1 */ for (unsigned int n_batch = 0; n_batch < m_n_batches; ++n_batch) { unsigned char* data1_ptr = m_bo_1_data + n_batch * m_bo_1_batch_size + m_bo_1_offset_double; unsigned char* data2_ptr = data1_ptr + sizeof(double); u.d = (double)(2 * n_batch); memcpy(data1_ptr, u.c, sizeof(double)); u.d = (double)(2 * n_batch + 1); memcpy(data2_ptr, u.c, sizeof(double)); } /* Fill signed byte data in BO2 */ for (unsigned int n_element = 0; n_element < m_n_batches; ++n_element) { signed char* data_ptr = (signed char*)(m_bo_2_data + n_element * m_bo_2_batch_size + m_bo_2_offset_sbyte); *data_ptr = (signed char)n_element; } /* Fill 4x2 matrix data in BO2 */ for (unsigned int n_matrix = 0; n_matrix < m_n_batches; ++n_matrix) { unsigned char* matrix_ptr = m_bo_2_data + n_matrix * m_bo_2_batch_size + m_bo_2_offset_matrix; for (unsigned int n_element = 0; n_element < 8 /* 4x2 matrix */; ++n_element) { u.d = (double)(n_matrix * 4 * 2 + n_element); memcpy(matrix_ptr + (sizeof(double) * n_element), u.c, sizeof(double)); } } /* for (all matrices) */ } /** Initializes a program object used by the test. * * This function may throw error exceptions if GL implementation misbehaves. * **/ void VAOTest::initProgramObject() { DE_ASSERT(m_po_id == 0); DE_ASSERT(m_vs_id == 0); /* Generate a program object */ m_po_id = gl.createProgram(); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() call failed."); /* Configure XFB */ const char* xfb_varyings[] = { "out_bo1_dmat3", "out_bo1_double", "out_bo1_int", "out_bo1_dvec2", "out_bo1_float2", "out_bo2_dmat4x2", "out_bo2_float", "gl_SkipComponents1" }; const unsigned int n_xfb_varyings = sizeof(xfb_varyings) / sizeof(xfb_varyings[0]); gl.transformFeedbackVaryings(m_po_id, n_xfb_varyings, xfb_varyings, GL_INTERLEAVED_ATTRIBS); GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings() call failed."); /* Initialize XFB-specific offset information for the verification routine */ m_xfb_bo1_dmat3_offset = 0; m_xfb_bo1_dmat3_size = sizeof(double) * 3 * 3; m_xfb_bo1_double_offset = m_xfb_bo1_dmat3_offset + m_xfb_bo1_dmat3_size; m_xfb_bo1_double_size = sizeof(double); m_xfb_bo1_int_offset = m_xfb_bo1_double_offset + m_xfb_bo1_double_size; m_xfb_bo1_int_size = sizeof(int); m_xfb_bo1_dvec2_offset = m_xfb_bo1_int_offset + m_xfb_bo1_int_size; m_xfb_bo1_dvec2_size = sizeof(double) * 2; m_xfb_bo1_float2_offset = m_xfb_bo1_dvec2_offset + m_xfb_bo1_dvec2_size; m_xfb_bo1_float2_size = sizeof(float); m_xfb_bo2_dmat4x2_offset = m_xfb_bo1_float2_offset + m_xfb_bo1_float2_size; m_xfb_bo2_dmat4x2_size = sizeof(double) * 4 * 2; m_xfb_bo2_float_offset = m_xfb_bo2_dmat4x2_offset + m_xfb_bo2_dmat4x2_size; m_xfb_bo2_float_size = sizeof(float); m_xfb_total_size = m_xfb_bo1_dmat3_size + m_xfb_bo1_double_size + m_xfb_bo1_int_size + m_xfb_bo1_dvec2_size + m_xfb_bo1_float2_size + m_xfb_bo2_dmat4x2_size + m_xfb_bo2_float_size + sizeof(int); /* Build the test program object */ const char* vs_code = "#version 400\n" "\n" "#extension GL_ARB_vertex_attrib_64bit : require\n" "\n" "in dmat3 in_bo1_dmat3;\n" "in double in_bo1_double;\n" "in dvec2 in_bo1_dvec2;\n" "in float in_bo1_float2;\n" "in int in_bo1_int;\n" "in dmat4x2 in_bo2_dmat4x2;\n" "in float in_bo2_float;\n" "\n" "out dmat3 out_bo1_dmat3;\n" "out double out_bo1_double;\n" "out dvec2 out_bo1_dvec2;\n" "out float out_bo1_float2;\n" "out int out_bo1_int;\n" "out dmat4x2 out_bo2_dmat4x2;\n" "out float out_bo2_float;\n" "\n" "void main()\n" "{\n" " out_bo1_dmat3 = in_bo1_dmat3;\n" " out_bo1_double = in_bo1_double;\n" " out_bo1_dvec2 = in_bo1_dvec2;\n" " out_bo1_int = in_bo1_int;\n" " out_bo1_float2 = in_bo1_float2;\n" " out_bo2_dmat4x2 = in_bo2_dmat4x2;\n" " out_bo2_float = in_bo2_float;\n" "}\n"; BuildProgramVSOnly(m_po_id, vs_code, m_vs_id); m_po_bo1_dmat3_attr_location = gl.getAttribLocation(m_po_id, "in_bo1_dmat3"); m_po_bo1_double_attr_location = gl.getAttribLocation(m_po_id, "in_bo1_double"); m_po_bo1_dvec2_attr_location = gl.getAttribLocation(m_po_id, "in_bo1_dvec2"); m_po_bo1_float2_attr_location = gl.getAttribLocation(m_po_id, "in_bo1_float2"); m_po_bo1_int_attr_location = gl.getAttribLocation(m_po_id, "in_bo1_int"); m_po_bo2_dmat4x2_attr_location = gl.getAttribLocation(m_po_id, "in_bo2_dmat4x2"); m_po_bo2_float_attr_location = gl.getAttribLocation(m_po_id, "in_bo2_float"); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetAttribLocation() call(s) failed."); if (m_po_bo1_dmat3_attr_location == -1 || m_po_bo1_double_attr_location == -1 || m_po_bo1_dvec2_attr_location == -1 || m_po_bo1_int_attr_location == -1 || m_po_bo1_float2_attr_location == -1 || m_po_bo2_dmat4x2_attr_location == -1 || m_po_bo2_float_attr_location == -1) { TCU_FAIL("At least one attribute is considered inactive which is invalid."); } } /** Initializes a vertex array object used by the test. * * This function may throw error exceptions if GL implementation misbehaves. **/ void VAOTest::initVAO() { gl.genVertexArrays(1, &m_vao_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenVertexArrays() call failed."); gl.bindVertexArray(m_vao_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray() call failed."); /* Set up BO1-sourced attributes */ gl.bindBuffer(GL_ARRAY_BUFFER, m_bo_id_1); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call failed."); gl.vertexAttribLPointer(m_po_bo1_dmat3_attr_location + 0, 3, /* size */ GL_DOUBLE, m_po_bo1_dmat3_attr_stride, (const glw::GLvoid*)(deUintptr)m_po_bo1_dmat3_attr_offset); gl.vertexAttribLPointer(m_po_bo1_dmat3_attr_location + 1, 3, /* size */ GL_DOUBLE, m_po_bo1_dmat3_attr_stride, (const glw::GLvoid*)(m_po_bo1_dmat3_attr_offset + 1 * sizeof(double) * 3)); gl.vertexAttribLPointer(m_po_bo1_dmat3_attr_location + 2, 3, /* size */ GL_DOUBLE, m_po_bo1_dmat3_attr_stride, (const glw::GLvoid*)(m_po_bo1_dmat3_attr_offset + 2 * sizeof(double) * 3)); GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribLPointer() call(s) failed."); gl.enableVertexAttribArray(m_po_bo1_dmat3_attr_location + 0); gl.enableVertexAttribArray(m_po_bo1_dmat3_attr_location + 1); gl.enableVertexAttribArray(m_po_bo1_dmat3_attr_location + 2); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray() call(s) failed."); gl.vertexAttribLPointer(m_po_bo1_dvec2_attr_location, 2, /* size */ GL_DOUBLE, m_po_bo1_dvec2_attr_stride, (const glw::GLvoid*)(deUintptr)m_po_bo1_dvec2_attr_offset); GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribLPointer() call failed."); gl.enableVertexAttribArray(m_po_bo1_dvec2_attr_location); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray() call failed."); gl.vertexAttribLPointer(m_po_bo1_double_attr_location, 1, /* size */ GL_DOUBLE, m_po_bo1_double_attr_stride, (const glw::GLvoid*)(deUintptr)m_po_bo1_double_attr_offset); GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribLPointer() call failed."); gl.enableVertexAttribArray(m_po_bo1_double_attr_location); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray() call failed."); gl.vertexAttribPointer(m_po_bo1_float2_attr_location, 1, /* size */ GL_SHORT, GL_FALSE, /* normalized */ m_po_bo1_float2_attr_stride, (const glw::GLvoid*)(deUintptr)m_po_bo1_float2_attr_offset); GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribPointer() call failed."); gl.enableVertexAttribArray(m_po_bo1_float2_attr_location); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray() call failed."); gl.vertexAttribIPointer(m_po_bo1_int_attr_location, 1, /* size */ GL_BYTE, m_po_bo1_int_attr_stride, (const glw::GLvoid*)(deUintptr)m_po_bo1_int_attr_offset); GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribIPointer() call failed."); gl.enableVertexAttribArray(m_po_bo1_int_attr_location); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray() call failed."); /* Set up BO2-sourced attributes */ gl.bindBuffer(GL_ARRAY_BUFFER, m_bo_id_2); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call failed."); gl.vertexAttribPointer(m_po_bo2_float_attr_location, 1, /* size */ GL_UNSIGNED_BYTE, GL_TRUE, m_po_bo2_float_attr_stride, (const glw::GLvoid*)(deUintptr)m_po_bo2_float_attr_offset); GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribPointer() call failed."); gl.enableVertexAttribArray(m_po_bo2_float_attr_location); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray() call failed."); gl.vertexAttribLPointer(m_po_bo2_dmat4x2_attr_location + 0, 2, /* size */ GL_DOUBLE, m_po_bo2_dmat4x2_attr_stride, (const glw::GLvoid*)(deUintptr)m_po_bo2_dmat4x2_attr_offset); gl.vertexAttribLPointer(m_po_bo2_dmat4x2_attr_location + 1, 2, /* size */ GL_DOUBLE, m_po_bo2_dmat4x2_attr_stride, (const glw::GLvoid*)(m_po_bo2_dmat4x2_attr_offset + 2 * sizeof(double))); gl.vertexAttribLPointer(m_po_bo2_dmat4x2_attr_location + 2, 2, /* size */ GL_DOUBLE, m_po_bo2_dmat4x2_attr_stride, (const glw::GLvoid*)(m_po_bo2_dmat4x2_attr_offset + 4 * sizeof(double))); gl.vertexAttribLPointer(m_po_bo2_dmat4x2_attr_location + 3, 2, /* size */ GL_DOUBLE, m_po_bo2_dmat4x2_attr_stride, (const glw::GLvoid*)(m_po_bo2_dmat4x2_attr_offset + 6 * sizeof(double))); GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribLPointer() call failed."); gl.enableVertexAttribArray(m_po_bo2_dmat4x2_attr_location + 0); gl.enableVertexAttribArray(m_po_bo2_dmat4x2_attr_location + 1); gl.enableVertexAttribArray(m_po_bo2_dmat4x2_attr_location + 2); gl.enableVertexAttribArray(m_po_bo2_dmat4x2_attr_location + 3); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray() call(s) failed."); /* Set up element binding */ gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_bo_id_indices); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call failed."); } /** Executes the test * * @return tcu::TestNode::STOP **/ tcu::TestNode::IterateResult VAOTest::iterate() { IterateStart(); bool result = true; RequireExtension("GL_ARB_vertex_attrib_64bit"); /* Initialize GL objects required to run the test */ initBuffers(); initBufferObjects(); initProgramObject(); initVAO(); /* Activate the program object before we continue */ gl.useProgram(m_po_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() call failed."); /* Iterate through all draw call combinations */ for (int n_draw_call_type = 0; n_draw_call_type < DRAW_CALL_TYPE_COUNT; ++n_draw_call_type) { _draw_call_type draw_call = (_draw_call_type)n_draw_call_type; for (int n_instanced_draw_call = 0; n_instanced_draw_call <= 1; /* false & true */ ++n_instanced_draw_call) { bool instanced_draw_call = (n_instanced_draw_call == 1); for (int n_vertex_attrib_divisor = 0; n_vertex_attrib_divisor <= 1; /* 0 & non-zero divisor */ ++n_vertex_attrib_divisor) { bool zero_vertex_attrib_divisor = (n_vertex_attrib_divisor == 0); /* Execute the test */ result &= executeTest(draw_call, instanced_draw_call, zero_vertex_attrib_divisor); } /* for (two vertex attrib divisor configurations) */ } /* for (non-instanced & instanced draw calls) */ } /* for (array-based & indiced draw calls) */ /* Done */ return IterateStop(result); } /** Verifies data that has been XFBed out by the draw call. * * @param data XFBed data. Must not be NULL. * @param draw_call Type of the draw call that was issued. * @param instanced True if the draw call was instanced, false otherwise. * @param zero_vertex_attrib_divisor True if a zero divisor was used for all checked attributes, * false if the divisors were set to a value of m_nonzero_vertex_attrib_divisor. */ bool VAOTest::verifyXFBData(const void* data, _draw_call_type draw_call, bool instanced, bool zero_vertex_attrib_divisor) { const float epsilon = 1e-5f; bool is_indiced = (draw_call == DRAW_CALL_TYPE_ELEMENTS); const unsigned int n_instances = (instanced) ? m_n_draw_call_instances : 1; bool result = true; const unsigned char* xfb_data_ptr = (const unsigned char*)data; for (unsigned int n_instance = 0; n_instance < n_instances; ++n_instance) { /* Verify dmat3 data from BO1 has been exposed correctly */ for (unsigned int n_element = 0; n_element < m_n_batches; ++n_element) { unsigned int in_index = n_element; unsigned int xfb_index = is_indiced ? m_bo_index_data[n_element] : n_element; if (!zero_vertex_attrib_divisor) { in_index = n_instance / m_nonzero_vertex_attrib_divisor; } const double* in_matrix_data_ptr = (const double*)(m_bo_1_data + (in_index)*m_po_bo1_dmat3_attr_stride + m_po_bo1_dmat3_attr_offset); const double* xfb_matrix_data_ptr = (const double*)(xfb_data_ptr + (m_n_batches * n_instance + xfb_index) * m_xfb_total_size + m_xfb_bo1_dmat3_offset); if (memcmp(in_matrix_data_ptr, xfb_matrix_data_ptr, m_xfb_bo1_dmat3_size) != 0) { m_testCtx.getLog() << tcu::TestLog::Message << "BO1 dmat3 attribute values mismatch for batch [" << n_element << "]" ", expected:[" << in_matrix_data_ptr[0] << ", " << in_matrix_data_ptr[1] << ", " << in_matrix_data_ptr[2] << ", " << in_matrix_data_ptr[3] << ", " << in_matrix_data_ptr[4] << ", " << in_matrix_data_ptr[5] << ", " << in_matrix_data_ptr[6] << ", " << in_matrix_data_ptr[7] << ", " << in_matrix_data_ptr[8] << ", " << "], XFBed out:[" << xfb_matrix_data_ptr[0] << ", " << xfb_matrix_data_ptr[1] << ", " << xfb_matrix_data_ptr[2] << ", " << xfb_matrix_data_ptr[3] << ", " << xfb_matrix_data_ptr[4] << ", " << xfb_matrix_data_ptr[5] << ", " << xfb_matrix_data_ptr[6] << ", " << xfb_matrix_data_ptr[7] << ", " << xfb_matrix_data_ptr[8] << ", " << "]" << tcu::TestLog::EndMessage; result = false; break; } } /* Verify float data from BO2 has been exposed correctly */ for (unsigned int n_batch = 0; n_batch < m_n_batches; ++n_batch) { unsigned int in_index = n_batch; const unsigned int xfb_index = is_indiced ? m_bo_index_data[n_batch] : n_batch; if (!zero_vertex_attrib_divisor) { in_index = n_instance / m_nonzero_vertex_attrib_divisor; } const unsigned char* in_ubyte_data_ptr = (const unsigned char*)(m_bo_2_data + (in_index)*m_po_bo2_float_attr_stride + m_po_bo2_float_attr_offset); const float* xfb_float_data_ptr = (const float*)(xfb_data_ptr + (m_n_batches * n_instance + xfb_index) * m_xfb_total_size + m_xfb_bo2_float_offset); float expected_value = ((float)*in_ubyte_data_ptr / 255.0f); if (de::abs(expected_value - *xfb_float_data_ptr) > epsilon) { m_testCtx.getLog() << tcu::TestLog::Message << "BO2 float attribute value mismatch for batch [" << n_batch << "]" ", expected: [" << expected_value << "]" ", XFBed out:[" << *xfb_float_data_ptr << "]" << tcu::TestLog::EndMessage; result = false; break; } } /* Verify dvec2 data from BO1 has been exposed correctly */ for (unsigned int n_element = 0; n_element < m_n_batches; ++n_element) { unsigned int in_index = n_element; const unsigned int xfb_index = is_indiced ? m_bo_index_data[n_element] : n_element; if (!zero_vertex_attrib_divisor) { in_index = n_instance / m_nonzero_vertex_attrib_divisor; } const double* in_dvec2_data_ptr = (const double*)(m_bo_1_data + (in_index)*m_po_bo1_dvec2_attr_stride + m_po_bo1_dvec2_attr_offset); const double* xfb_dvec2_data_ptr = (const double*)(xfb_data_ptr + (m_n_batches * n_instance + xfb_index) * m_xfb_total_size + m_xfb_bo1_dvec2_offset); if (memcmp(in_dvec2_data_ptr, xfb_dvec2_data_ptr, m_xfb_bo1_dvec2_size) != 0) { m_testCtx.getLog() << tcu::TestLog::Message << "BO1 dvec2 attribute values mismatch for batch [" << n_element << "]" ", expected:[" << in_dvec2_data_ptr[0] << ", " << in_dvec2_data_ptr[1] << ", " << "], XFBed out:[" << xfb_dvec2_data_ptr[0] << ", " << xfb_dvec2_data_ptr[1] << ", " << "]" << tcu::TestLog::EndMessage; result = false; break; } } /* Verify double data from BO1 has been exposed correctly */ for (unsigned int n_element = 0; n_element < m_n_batches; ++n_element) { unsigned int in_index = n_element; const unsigned int xfb_index = is_indiced ? m_bo_index_data[n_element] : n_element; if (!zero_vertex_attrib_divisor) { in_index = n_instance / m_nonzero_vertex_attrib_divisor; } const double* in_double_data_ptr = (const double*)(m_bo_1_data + (in_index)*m_po_bo1_double_attr_stride + m_po_bo1_double_attr_offset); const double* xfb_double_data_ptr = (const double*)(xfb_data_ptr + (m_n_batches * n_instance + xfb_index) * m_xfb_total_size + m_xfb_bo1_double_offset); if (memcmp(in_double_data_ptr, xfb_double_data_ptr, m_xfb_bo1_double_size) != 0) { m_testCtx.getLog() << tcu::TestLog::Message << "BO1 double attribute value mismatch for batch [" << n_element << "]" ", expected: [" << *in_double_data_ptr << "]" ", XFBed out:[" << *xfb_double_data_ptr << "]" << tcu::TestLog::EndMessage; result = false; break; } } /* Verify dmat4x2 data from BO2 has been exposed correctly */ for (unsigned int n_element = 0; n_element < m_n_batches; ++n_element) { unsigned int in_index = n_element; const unsigned int xfb_index = is_indiced ? m_bo_index_data[n_element] : n_element; if (!zero_vertex_attrib_divisor) { in_index = n_instance / m_nonzero_vertex_attrib_divisor; } const unsigned char* in_matrix_data_ptr = m_bo_2_data + (in_index)*m_po_bo2_dmat4x2_attr_stride + m_po_bo2_dmat4x2_attr_offset; const unsigned char* xfb_matrix_data_ptr = xfb_data_ptr + (m_n_batches * n_instance + xfb_index) * m_xfb_total_size + m_xfb_bo2_dmat4x2_offset; if (memcmp(in_matrix_data_ptr, xfb_matrix_data_ptr, m_xfb_bo2_dmat4x2_size) != 0) { m_testCtx.getLog() << tcu::TestLog::Message << "BO2 dmat4x2 attribute values mismatch for batch [" << n_element << "]" ", expected:[" << in_matrix_data_ptr[0] << ", " << in_matrix_data_ptr[1] << ", " << in_matrix_data_ptr[2] << ", " << in_matrix_data_ptr[3] << ", " << in_matrix_data_ptr[4] << ", " << in_matrix_data_ptr[5] << ", " << in_matrix_data_ptr[6] << ", " << in_matrix_data_ptr[7] << ", " << "], XFBed out:[" << xfb_matrix_data_ptr[0] << ", " << xfb_matrix_data_ptr[1] << ", " << xfb_matrix_data_ptr[2] << ", " << xfb_matrix_data_ptr[3] << ", " << xfb_matrix_data_ptr[4] << ", " << xfb_matrix_data_ptr[5] << ", " << xfb_matrix_data_ptr[6] << ", " << xfb_matrix_data_ptr[7] << ", " << "]" << tcu::TestLog::EndMessage; result = false; break; } } /* Verify int data from BO1 has been exposed correctly */ for (unsigned int n_element = 0; n_element < m_n_batches; ++n_element) { unsigned int in_index = n_element; const unsigned int xfb_index = is_indiced ? m_bo_index_data[n_element] : n_element; if (!zero_vertex_attrib_divisor) { in_index = n_instance / m_nonzero_vertex_attrib_divisor; } const signed char* in_char_data_ptr = (const signed char*)(m_bo_1_data + (in_index)*m_po_bo1_int_attr_stride + m_po_bo1_int_attr_offset); const signed int* xfb_int_data_ptr = (const signed int*)(xfb_data_ptr + (m_n_batches * n_instance + xfb_index) * m_xfb_total_size + m_xfb_bo1_int_offset); if (de::abs((signed int)*in_char_data_ptr - *xfb_int_data_ptr) > 0) { m_testCtx.getLog() << tcu::TestLog::Message << "BO1 int attribute value mismatch for batch [" << n_element << "]" ", expected: [" << (signed int)*in_char_data_ptr << "]" ", XFBed out:[" << *xfb_int_data_ptr << "]" << tcu::TestLog::EndMessage; result = false; break; } } /* Verify float data from BO1 has been exposed correctly */ for (unsigned int n_element = 0; n_element < m_n_batches; ++n_element) { unsigned int in_index = n_element; const unsigned int xfb_index = is_indiced ? m_bo_index_data[n_element] : n_element; if (!zero_vertex_attrib_divisor) { in_index = n_instance / m_nonzero_vertex_attrib_divisor; } const unsigned short* in_short_data_ptr = (const unsigned short*)(m_bo_1_data + (in_index)*m_po_bo1_float2_attr_stride + m_po_bo1_float2_attr_offset); const float* xfb_float_data_ptr = (const float*)(xfb_data_ptr + (m_n_batches * n_instance + xfb_index) * m_xfb_total_size + m_xfb_bo1_float2_offset); if (de::abs(*in_short_data_ptr - *xfb_float_data_ptr) > epsilon) { m_testCtx.getLog() << tcu::TestLog::Message << "BO1 float attribute value mismatch for batch [" << n_element << "]" ", expected: [" << (signed int)*in_short_data_ptr << "]" ", XFBed out:[" << *xfb_float_data_ptr << "]" << tcu::TestLog::EndMessage; result = false; break; } } } /* for (all instances) */ return result; } } /* namespace VertexAttrib64Bit */ namespace gl4cts { VertexAttrib64BitTests::VertexAttrib64BitTests(deqp::Context& context) : TestCaseGroup(context, "vertex_attrib_64bit", "Verifes GL_ARB_vertex_attrib_64bit functionality") { /* Nothing to be done here */ } void VertexAttrib64BitTests::init(void) { addChild(new VertexAttrib64Bit::ApiErrorsTest(m_context)); addChild(new VertexAttrib64Bit::GetVertexAttribTest(m_context)); addChild(new VertexAttrib64Bit::LimitTest(m_context)); addChild(new VertexAttrib64Bit::VAOTest(m_context)); } } /* namespace gl4cts */