blob: 0b2823bea80f8bee1ea596d90e122c5356fecc85 [file] [log] [blame]
/*-------------------------------------------------------------------------
* 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 <algorithm>
#include <iomanip>
#include <string>
#include <vector>
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<GLchar> 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<GLchar> 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 <min:max>
*
* @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<glw::GLushort>((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<std::string>& 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 <index> is greater than or equal to GL_MAX_VERTEX_ATTRIBS;
*
* b) GL_INVALID_ENUM should be generated by glVertexAttribLPointer()
* if <type> is not GL_DOUBLE;
*
* c) GL_INVALID_VALUE should be generated by glVertexAttribLPointer()
* if <size> is not 1, 2, 3 or 4.
*
* d) GL_INVALID_VALUE should be generated by glVertexAttribLPointer()
* if <stride> 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 <pointer> argument is not NULL.
*
* f) GL_INVALID_OPERATION should be generated by glGetVertexAttribLdv()
* if <index> 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 <type> 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 <pointer> 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 <index> 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 <size> 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 <stride> 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 <GLuint SIZE>
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 <GLuint SIZE>
void checkVertexAttribLd(GLuint index, bool& result) const;
template <GLuint SIZE>
void checkVertexAttribLdv(GLuint index, bool& result) const;
void checkVertexAttribLPointer(GLuint index, bool& result) const;
/* Wrappers for vertexAttribLd routines */
template <GLuint SIZE>
void vertexAttribLd(GLuint index, const vertexAttribute<SIZE>& attribute) const;
template <GLuint SIZE>
void vertexAttribLdv(GLuint index, const vertexAttribute<SIZE>& 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 <GLuint SIZE>
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<SIZE> vertex_attribute(m_min, m_max);
vertexAttribLd<SIZE>(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 <GLuint SIZE>
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<SIZE> vertex_attribute(m_min, m_max);
vertexAttribLdv<SIZE>(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, &params_getVertexAttribiv);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetVertexAttribiv");
gl.getVertexAttribLdv(index, pname, &params_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<GLdouble>& out_buffer_data) const;
void setAttributes_a(const attributeConfiguration& configuration, GLint n_type, GLuint vertex,
std::vector<GLdouble>& out_buffer_data) const;
void setAttributes_a_scalar(const attributeConfiguration& configuration, GLint n_type, GLuint vertex,
std::vector<GLdouble>& out_buffer_data) const;
void setAttributes_a_vec(const attributeConfiguration& configuration, GLint n_type, GLuint vertex,
std::vector<GLdouble>& out_buffer_data) const;
void setAttributes_b(const attributeConfiguration& configuration, GLint n_type, GLuint vertex,
std::vector<GLdouble>& out_buffer_dataa) const;
void setAttributes_c(const attributeConfiguration& configuration, GLint n_type, GLuint vertex,
std::vector<GLdouble>& 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<glw::GLsizei>(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<GLdouble> 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 <vertex>
*
* @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<GLdouble>& 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 <n_type> for <vertex>, 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<GLdouble>& 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 <n_type> for <vertex>, 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<GLdouble>& 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 <n_type> for <vertex>, 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<GLdouble>& 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 <n_type> for <vertex>, 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<GLdouble>& 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 <n_type> for <vertex>, 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<GLdouble>& 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<GLuint> 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: <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<unsigned int>((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<unsigned int>(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, in_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) > epsilon)
{
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 */