blob: bb6c9fa4cd2b86972a8899bbba687652275aad97 [file] [log] [blame]
/*-------------------------------------------------------------------------
* OpenGL Conformance Test Suite
* -----------------------------
*
* Copyright (c) 2015-2016 The Khronos Group Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/ /*!
* \file
* \brief
*/ /*-------------------------------------------------------------------*/
/**
* \file gl4cEnhancedLayoutsTests.cpp
* \brief Implements conformance tests for "Enhanced Layouts" functionality.
*/ /*-------------------------------------------------------------------*/
#include "gl4cEnhancedLayoutsTests.hpp"
#include "gluContextInfo.hpp"
#include "gluDefs.hpp"
#include "gluStrUtil.hpp"
#include "glwEnums.hpp"
#include "glwFunctions.hpp"
#include "tcuTestLog.hpp"
#include <algorithm>
#include <iomanip>
#include <string>
#include <vector>
/* DEBUG */
#define USE_NSIGHT 0
#define DEBUG_ENBALE_MESSAGE_CALLBACK 0
#define DEBUG_NEG_LOG_ERROR 0
#define DEBUG_REPLACE_TOKEN 0
#define DEBUG_REPEAT_TEST_CASE 0
#define DEBUG_REPEATED_TEST_CASE 0
/* Texture test base */
#define DEBUG_TTB_VERIFICATION_SNIPPET_STAGE 0
#define DEBUG_TTB_VERIFICATION_SNIPPET_VARIABLE 0
/* Tests */
#define DEBUG_VERTEX_ATTRIB_LOCATIONS_TEST_VARIABLE 0
/* WORKAROUNDS */
#define WRKARD_UNIFORMBLOCKMEMBEROFFSETANDALIGNTEST 0
#define WRKARD_UNIFORMBLOCKMEMBERALIGNNONPOWEROF2TEST 0
#define WRKARD_UNIFORMBLOCKALIGNMENT 0
#define WRKARD_VARYINGLOCATIONSTEST 0
using namespace glw;
namespace gl4cts
{
namespace EnhancedLayouts
{
namespace Utils
{
/** Constants used by "random" generators **/
static const GLuint s_rand_start = 3;
static const GLuint s_rand_max = 16;
static const GLuint s_rand_max_half = s_rand_max / 2;
/** Seed used by "random" generators **/
static GLuint s_rand = s_rand_start;
/** Get "random" unsigned int value
*
* @return Value
**/
static GLuint GetRandUint()
{
const GLuint rand = s_rand++;
if (s_rand_max <= s_rand)
{
s_rand = s_rand_start;
}
return rand;
}
/** Get "random" int value
*
* @return Value
**/
GLint GetRandInt()
{
const GLint rand = GetRandUint() - s_rand_max_half;
return rand;
}
/** Get "random" double value
*
* @return Value
**/
GLdouble GetRandDouble()
{
const GLint rand = GetRandInt();
GLdouble result = (GLfloat)rand / (GLdouble)s_rand_max_half;
return result;
}
/** Get "random" float value
*
* @return Value
**/
GLfloat GetRandFloat()
{
const GLint rand = GetRandInt();
GLfloat result = (GLfloat)rand / (GLfloat)s_rand_max_half;
return result;
}
/** String used by list routines **/
static const GLchar* const g_list = "LIST";
/** Type constants **/
const Type Type::_double = Type::GetType(Type::Double, 1, 1);
const Type Type::dmat2 = Type::GetType(Type::Double, 2, 2);
const Type Type::dmat2x3 = Type::GetType(Type::Double, 2, 3);
const Type Type::dmat2x4 = Type::GetType(Type::Double, 2, 4);
const Type Type::dmat3x2 = Type::GetType(Type::Double, 3, 2);
const Type Type::dmat3 = Type::GetType(Type::Double, 3, 3);
const Type Type::dmat3x4 = Type::GetType(Type::Double, 3, 4);
const Type Type::dmat4x2 = Type::GetType(Type::Double, 4, 2);
const Type Type::dmat4x3 = Type::GetType(Type::Double, 4, 3);
const Type Type::dmat4 = Type::GetType(Type::Double, 4, 4);
const Type Type::dvec2 = Type::GetType(Type::Double, 1, 2);
const Type Type::dvec3 = Type::GetType(Type::Double, 1, 3);
const Type Type::dvec4 = Type::GetType(Type::Double, 1, 4);
const Type Type::_int = Type::GetType(Type::Int, 1, 1);
const Type Type::ivec2 = Type::GetType(Type::Int, 1, 2);
const Type Type::ivec3 = Type::GetType(Type::Int, 1, 3);
const Type Type::ivec4 = Type::GetType(Type::Int, 1, 4);
const Type Type::_float = Type::GetType(Type::Float, 1, 1);
const Type Type::mat2 = Type::GetType(Type::Float, 2, 2);
const Type Type::mat2x3 = Type::GetType(Type::Float, 2, 3);
const Type Type::mat2x4 = Type::GetType(Type::Float, 2, 4);
const Type Type::mat3x2 = Type::GetType(Type::Float, 3, 2);
const Type Type::mat3 = Type::GetType(Type::Float, 3, 3);
const Type Type::mat3x4 = Type::GetType(Type::Float, 3, 4);
const Type Type::mat4x2 = Type::GetType(Type::Float, 4, 2);
const Type Type::mat4x3 = Type::GetType(Type::Float, 4, 3);
const Type Type::mat4 = Type::GetType(Type::Float, 4, 4);
const Type Type::vec2 = Type::GetType(Type::Float, 1, 2);
const Type Type::vec3 = Type::GetType(Type::Float, 1, 3);
const Type Type::vec4 = Type::GetType(Type::Float, 1, 4);
const Type Type::uint = Type::GetType(Type::Uint, 1, 1);
const Type Type::uvec2 = Type::GetType(Type::Uint, 1, 2);
const Type Type::uvec3 = Type::GetType(Type::Uint, 1, 3);
const Type Type::uvec4 = Type::GetType(Type::Uint, 1, 4);
/** Generate data for type. This routine follows STD140 rules
*
* @return Vector of bytes filled with data
**/
std::vector<GLubyte> Type::GenerateData() const
{
const GLuint alignment = GetActualAlignment(0, false);
std::vector<GLubyte> data;
data.resize(alignment * m_n_columns);
for (GLuint column = 0; column < m_n_columns; ++column)
{
GLvoid* ptr = (GLvoid*)&data[column * alignment];
switch (m_basic_type)
{
case Double:
{
GLdouble* d_ptr = (GLdouble*)ptr;
for (GLuint i = 0; i < m_n_rows; ++i)
{
d_ptr[i] = GetRandDouble();
}
}
break;
case Float:
{
GLfloat* f_ptr = (GLfloat*)ptr;
for (GLuint i = 0; i < m_n_rows; ++i)
{
f_ptr[i] = GetRandFloat();
}
}
break;
case Int:
{
GLint* i_ptr = (GLint*)ptr;
for (GLuint i = 0; i < m_n_rows; ++i)
{
i_ptr[i] = GetRandInt();
}
}
break;
case Uint:
{
GLuint* ui_ptr = (GLuint*)ptr;
for (GLuint i = 0; i < m_n_rows; ++i)
{
ui_ptr[i] = GetRandUint();
}
}
break;
}
}
return data;
}
/** Generate data for type. This routine packs data tightly.
*
* @return Vector of bytes filled with data
**/
std::vector<GLubyte> Type::GenerateDataPacked() const
{
const GLuint basic_size = GetTypeSize(m_basic_type);
const GLuint n_elements = m_n_columns * m_n_rows;
const GLuint size = basic_size * n_elements;
std::vector<GLubyte> data;
data.resize(size);
GLvoid* ptr = (GLvoid*)&data[0];
switch (m_basic_type)
{
case Double:
{
GLdouble* d_ptr = (GLdouble*)ptr;
for (GLuint i = 0; i < n_elements; ++i)
{
d_ptr[i] = GetRandDouble();
}
}
break;
case Float:
{
GLfloat* f_ptr = (GLfloat*)ptr;
for (GLuint i = 0; i < n_elements; ++i)
{
f_ptr[i] = GetRandFloat();
}
}
break;
case Int:
{
GLint* i_ptr = (GLint*)ptr;
for (GLuint i = 0; i < n_elements; ++i)
{
i_ptr[i] = GetRandInt();
}
}
break;
case Uint:
{
GLuint* ui_ptr = (GLuint*)ptr;
for (GLuint i = 0; i < n_elements; ++i)
{
ui_ptr[i] = GetRandUint();
}
}
break;
}
return data;
}
/** Calculate "actual alignment". It work under assumption that align value is valid
*
* @param align Requested alignment, eg with "align" qualifier
* @param is_array Selects if an array of type or single instance should be considered
*
* @return Calculated value
**/
GLuint Type::GetActualAlignment(GLuint align, bool is_array) const
{
const GLuint base_alignment = GetBaseAlignment(is_array);
return std::max(align, base_alignment);
}
/** Align given ofset with specified alignment
*
* @param offset Offset
* @param alignment Alignment
*
* @return Calculated value
**/
GLuint align(GLuint offset, GLuint alignment)
{
const GLuint rest = offset % alignment;
if (0 != rest)
{
GLuint missing = alignment - rest;
offset += missing;
}
return offset;
}
/** Calculate "actual offset"
*
* @param start_offset Requested offset
* @param actual_alignment Actual alignemnt
*
* @return Calculated value
**/
GLuint Type::GetActualOffset(GLuint start_offset, GLuint actual_alignment)
{
GLuint offset = align(start_offset, actual_alignment);
return offset;
}
/** Calculate "base alignment" for given type
*
* @param is_array Select if array or single instance should be considered
*
* @return Calculated value
**/
GLuint Type::GetBaseAlignment(bool is_array) const
{
GLuint elements = 1;
switch (m_n_rows)
{
case 2:
elements = 2;
break;
case 3:
case 4:
elements = 4;
break;
default:
break;
}
GLuint N = GetTypeSize(m_basic_type);
GLuint alignment = N * elements;
if ((true == is_array) || (1 != m_n_columns))
{
alignment = align(alignment, 16 /* vec4 alignment */);
}
return alignment;
}
/** Returns string representing GLSL constructor of type with arguments provided in data
*
* @param data Array of values that will be used as construcotr arguments.
* It is interpreted as tightly packed array of type matching this type.
*
* @return String in form "Type(args)"
**/
std::string Type::GetGLSLConstructor(const GLvoid* data) const
{
const GLchar* type = GetGLSLTypeName();
std::stringstream stream;
stream << type << "(";
/* Scalar or vector */
if (1 == m_n_columns)
{
for (GLuint row = 0; row < m_n_rows; ++row)
{
switch (m_basic_type)
{
case Double:
stream << ((GLdouble*)data)[row];
break;
case Float:
stream << ((GLfloat*)data)[row];
break;
case Int:
stream << ((GLint*)data)[row];
break;
case Uint:
stream << ((GLuint*)data)[row];
break;
}
if (row + 1 != m_n_rows)
{
stream << ", ";
}
}
}
else /* Matrix: mat(vec(), vec() .. ) */
{
const GLuint basic_size = GetTypeSize(m_basic_type);
// Very indescoverable defect, the column stride should be calculated by rows, such as mat2x3, which is 2, columns 3 rows, its column stride should be 3 * sizeof(float)
const GLuint column_stride = m_n_rows * basic_size;
const Type column_type = GetType(m_basic_type, 1, m_n_rows);
for (GLuint column = 0; column < m_n_columns; ++column)
{
const GLuint column_offset = column * column_stride;
const GLvoid* column_data = (GLubyte*)data + column_offset;
stream << column_type.GetGLSLConstructor(column_data);
if (column + 1 != m_n_columns)
{
stream << ", ";
}
}
}
stream << ")";
return stream.str();
}
/** Get glsl name of the type
*
* @return Name of glsl type
**/
const glw::GLchar* Type::GetGLSLTypeName() const
{
static const GLchar* float_lut[4][4] = {
{ "float", "vec2", "vec3", "vec4" },
{ 0, "mat2", "mat2x3", "mat2x4" },
{ 0, "mat3x2", "mat3", "mat3x4" },
{ 0, "mat4x2", "mat4x3", "mat4" },
};
static const GLchar* double_lut[4][4] = {
{ "double", "dvec2", "dvec3", "dvec4" },
{ 0, "dmat2", "dmat2x3", "dmat2x4" },
{ 0, "dmat3x2", "dmat3", "dmat3x4" },
{ 0, "dmat4x2", "dmat4x3", "dmat4" },
};
static const GLchar* int_lut[4] = { "int", "ivec2", "ivec3", "ivec4" };
static const GLchar* uint_lut[4] = { "uint", "uvec2", "uvec3", "uvec4" };
const GLchar* result = 0;
if ((1 > m_n_columns) || (1 > m_n_rows) || (4 < m_n_columns) || (4 < m_n_rows))
{
return 0;
}
switch (m_basic_type)
{
case Float:
result = float_lut[m_n_columns - 1][m_n_rows - 1];
break;
case Double:
result = double_lut[m_n_columns - 1][m_n_rows - 1];
break;
case Int:
result = int_lut[m_n_rows - 1];
break;
case Uint:
result = uint_lut[m_n_rows - 1];
break;
default:
TCU_FAIL("Invliad enum");
}
return result;
}
/** Get number of locations required for the type
*
* @return Number of columns times:
* - 2 when type is double with 3 or 4 rows,
* - 1 otherwise.
**/
GLuint Type::GetLocations() const
{
GLuint n_loc_per_column;
/* 1 or 2 doubles any for rest */
if ((2 >= m_n_rows) || (Double != m_basic_type))
{
n_loc_per_column = 1;
}
else
{
/* 3 and 4 doubles */
n_loc_per_column = 2;
}
return n_loc_per_column * m_n_columns;
}
/** Get size of the type in bytes. Note that this routine assumes tightly packing
*
* @return Formula Number of columns * number of rows * sizeof(base_type)
**/
GLuint Type::GetSize() const
{
const GLuint basic_type_size = GetTypeSize(m_basic_type);
const GLuint n_elements = m_n_columns * m_n_rows;
return basic_type_size * n_elements;
}
/** Get GLenum representing the type
*
* @return GLenum
**/
GLenum Type::GetTypeGLenum() const
{
static const GLenum float_lut[4][4] = {
{ GL_FLOAT, GL_FLOAT_VEC2, GL_FLOAT_VEC3, GL_FLOAT_VEC4 },
{ 0, GL_FLOAT_MAT2, GL_FLOAT_MAT2x3, GL_FLOAT_MAT2x4 },
{ 0, GL_FLOAT_MAT3x2, GL_FLOAT_MAT3, GL_FLOAT_MAT3x4 },
{ 0, GL_FLOAT_MAT4x2, GL_FLOAT_MAT4x3, GL_FLOAT_MAT4 },
};
static const GLenum double_lut[4][4] = {
{ GL_DOUBLE, GL_DOUBLE_VEC2, GL_DOUBLE_VEC3, GL_DOUBLE_VEC4 },
{ 0, GL_DOUBLE_MAT2, GL_DOUBLE_MAT2x3, GL_DOUBLE_MAT2x4 },
{ 0, GL_DOUBLE_MAT3x2, GL_DOUBLE_MAT3, GL_DOUBLE_MAT3x4 },
{ 0, GL_DOUBLE_MAT4x2, GL_DOUBLE_MAT4x3, GL_DOUBLE_MAT4 },
};
static const GLenum int_lut[4] = { GL_INT, GL_INT_VEC2, GL_INT_VEC3, GL_INT_VEC4 };
static const GLenum uint_lut[4] = { GL_UNSIGNED_INT, GL_UNSIGNED_INT_VEC2, GL_UNSIGNED_INT_VEC3,
GL_UNSIGNED_INT_VEC4 };
GLenum result = 0;
if ((1 > m_n_columns) || (1 > m_n_rows) || (4 < m_n_columns) || (4 < m_n_rows))
{
return 0;
}
switch (m_basic_type)
{
case Float:
result = float_lut[m_n_columns - 1][m_n_rows - 1];
break;
case Double:
result = double_lut[m_n_columns - 1][m_n_rows - 1];
break;
case Int:
result = int_lut[m_n_rows - 1];
break;
case Uint:
result = uint_lut[m_n_rows - 1];
break;
default:
TCU_FAIL("Invliad enum");
}
return result;
}
/** Calculate the numbe of components consumed by a type
* according to 11.1.2.1 Output Variables
*
* @return Calculated number of components for the type
**/
GLuint Type::GetNumComponents() const
{
// Rule 3 of Section 7.6.2.2
// If the member is a three-component vector with components consuming N
// basic machine units, the base alignment is 4N.
GLuint num_components = (m_n_rows == 3 ? 4 : m_n_rows) * m_n_columns;
if (m_basic_type == Double)
{
num_components *= 2;
}
return num_components;
}
/** Calculate stride for the type according to std140 rules
*
* @param alignment Alignment of type
* @param n_columns Number of columns
* @param n_array_elements Number of elements in array
*
* @return Calculated value
**/
GLuint Type::CalculateStd140Stride(GLuint alignment, GLuint n_columns, GLuint n_array_elements)
{
GLuint stride = alignment * n_columns;
if (0 != n_array_elements)
{
stride *= n_array_elements;
}
return stride;
}
/** Check if glsl support matrices for specific basic type
*
* @param type Basic type
*
* @return true if matrices of <type> are supported, false otherwise
**/
bool Type::DoesTypeSupportMatrix(TYPES type)
{
bool result = false;
switch (type)
{
case Float:
case Double:
result = true;
break;
case Int:
case Uint:
result = false;
break;
default:
TCU_FAIL("Invliad enum");
}
return result;
}
/** Creates instance of Type
*
* @param basic_type Select basic type of instance
* @param n_columns Number of columns
* @param n_rows Number of rows
*
* @return Type instance
**/
Type Type::GetType(TYPES basic_type, glw::GLuint n_columns, glw::GLuint n_rows)
{
Type type = { basic_type, n_columns, n_rows };
return type;
}
/** Get Size of given type in bytes
*
* @param type
*
* @return Size of type
**/
GLuint Type::GetTypeSize(TYPES type)
{
GLuint result = 0;
switch (type)
{
case Float:
result = sizeof(GLfloat);
break;
case Double:
result = sizeof(GLdouble);
break;
case Int:
result = sizeof(GLint);
break;
case Uint:
result = sizeof(GLuint);
break;
default:
TCU_FAIL("Invalid enum");
}
return result;
}
/** Get GLenum representing given type
*
* @param type
*
* @return GLenum value
**/
GLenum Type::GetTypeGLenum(TYPES type)
{
GLenum result = 0;
switch (type)
{
case Float:
result = GL_FLOAT;
break;
case Double:
result = GL_DOUBLE;
break;
case Int:
result = GL_INT;
break;
case Uint:
result = GL_UNSIGNED_INT;
break;
default:
TCU_FAIL("Invalid enum");
}
return result;
}
/** Get proper glUniformNdv routine for vectors with specified number of rows
*
* @param gl GL functions
* @param n_rows Number of rows
*
* @return Function address
**/
uniformNdv getUniformNdv(const glw::Functions& gl, glw::GLuint n_rows)
{
uniformNdv result = 0;
switch (n_rows)
{
case 1:
result = gl.uniform1dv;
break;
case 2:
result = gl.uniform2dv;
break;
case 3:
result = gl.uniform3dv;
break;
case 4:
result = gl.uniform4dv;
break;
default:
TCU_FAIL("Invalid number of rows");
}
return result;
}
/** Get proper glUniformNfv routine for vectors with specified number of rows
*
* @param gl GL functions
* @param n_rows Number of rows
*
* @return Function address
**/
uniformNfv getUniformNfv(const glw::Functions& gl, glw::GLuint n_rows)
{
uniformNfv result = 0;
switch (n_rows)
{
case 1:
result = gl.uniform1fv;
break;
case 2:
result = gl.uniform2fv;
break;
case 3:
result = gl.uniform3fv;
break;
case 4:
result = gl.uniform4fv;
break;
default:
TCU_FAIL("Invalid number of rows");
}
return result;
}
/** Get proper glUniformNiv routine for vectors with specified number of rows
*
* @param gl GL functions
* @param n_rows Number of rows
*
* @return Function address
**/
uniformNiv getUniformNiv(const glw::Functions& gl, glw::GLuint n_rows)
{
uniformNiv result = 0;
switch (n_rows)
{
case 1:
result = gl.uniform1iv;
break;
case 2:
result = gl.uniform2iv;
break;
case 3:
result = gl.uniform3iv;
break;
case 4:
result = gl.uniform4iv;
break;
default:
TCU_FAIL("Invalid number of rows");
}
return result;
}
/** Get proper glUniformNuiv routine for vectors with specified number of rows
*
* @param gl GL functions
* @param n_rows Number of rows
*
* @return Function address
**/
uniformNuiv getUniformNuiv(const glw::Functions& gl, glw::GLuint n_rows)
{
uniformNuiv result = 0;
switch (n_rows)
{
case 1:
result = gl.uniform1uiv;
break;
case 2:
result = gl.uniform2uiv;
break;
case 3:
result = gl.uniform3uiv;
break;
case 4:
result = gl.uniform4uiv;
break;
default:
TCU_FAIL("Invalid number of rows");
}
return result;
}
/** Get proper glUniformMatrixNdv routine for matrix with specified number of columns and rows
*
* @param gl GL functions
* @param n_rows Number of rows
*
* @return Function address
**/
uniformMatrixNdv getUniformMatrixNdv(const glw::Functions& gl, glw::GLuint n_columns, glw::GLuint n_rows)
{
uniformMatrixNdv result = 0;
switch (n_columns)
{
case 2:
switch (n_rows)
{
case 2:
result = gl.uniformMatrix2dv;
break;
case 3:
result = gl.uniformMatrix2x3dv;
break;
case 4:
result = gl.uniformMatrix2x4dv;
break;
default:
TCU_FAIL("Invalid number of rows");
}
break;
case 3:
switch (n_rows)
{
case 2:
result = gl.uniformMatrix3x2dv;
break;
case 3:
result = gl.uniformMatrix3dv;
break;
case 4:
result = gl.uniformMatrix3x4dv;
break;
default:
TCU_FAIL("Invalid number of rows");
}
break;
case 4:
switch (n_rows)
{
case 2:
result = gl.uniformMatrix4x2dv;
break;
case 3:
result = gl.uniformMatrix4x3dv;
break;
case 4:
result = gl.uniformMatrix4dv;
break;
default:
TCU_FAIL("Invalid number of rows");
}
break;
default:
TCU_FAIL("Invalid number of columns");
}
return result;
}
/** Get proper glUniformMatrixNfv routine for vectors with specified number of columns and rows
*
* @param gl GL functions
* @param n_rows Number of rows
*
* @return Function address
**/
uniformMatrixNfv getUniformMatrixNfv(const glw::Functions& gl, glw::GLuint n_columns, glw::GLuint n_rows)
{
uniformMatrixNfv result = 0;
switch (n_columns)
{
case 2:
switch (n_rows)
{
case 2:
result = gl.uniformMatrix2fv;
break;
case 3:
result = gl.uniformMatrix2x3fv;
break;
case 4:
result = gl.uniformMatrix2x4fv;
break;
default:
TCU_FAIL("Invalid number of rows");
}
break;
case 3:
switch (n_rows)
{
case 2:
result = gl.uniformMatrix3x2fv;
break;
case 3:
result = gl.uniformMatrix3fv;
break;
case 4:
result = gl.uniformMatrix3x4fv;
break;
default:
TCU_FAIL("Invalid number of rows");
}
break;
case 4:
switch (n_rows)
{
case 2:
result = gl.uniformMatrix4x2fv;
break;
case 3:
result = gl.uniformMatrix4x3fv;
break;
case 4:
result = gl.uniformMatrix4fv;
break;
default:
TCU_FAIL("Invalid number of rows");
}
break;
default:
TCU_FAIL("Invalid number of columns");
}
return result;
}
bool verifyVarying(Program& program, const std::string& parent_name, const Variable::Descriptor& desc,
std::stringstream& stream, bool is_input)
{
GLint component = 0;
GLuint index = 0;
GLenum interface = GL_PROGRAM_INPUT;
GLint location = 0;
if (false == is_input)
{
interface = GL_PROGRAM_OUTPUT;
}
const std::string& name = Utils::Variable::GetReference(parent_name, desc, Utils::Variable::BASIC, 0);
try
{
index = program.GetResourceIndex(name, interface);
program.GetResource(interface, index, GL_LOCATION, 1 /* size */, &location);
program.GetResource(interface, index, GL_LOCATION_COMPONENT, 1 /* size */, &component);
}
catch (std::exception& exc)
{
stream << "Failed to query program for varying: " << desc.m_name << ". Reason: " << exc.what() << "\n";
return false;
}
bool result = true;
if (location != desc.m_expected_location)
{
stream << "Attribute: " << desc.m_name << " - invalid location: " << location
<< " expected: " << desc.m_expected_location << std::endl;
result = false;
}
if (component != desc.m_expected_component)
{
stream << "Attribute: " << desc.m_name << " - invalid component: " << component
<< " expected: " << desc.m_expected_component << std::endl;
result = false;
}
return result;
}
/** Query program resource for given variable and verify that everything is as expected
*
* @param program Program object
* @param variable Variable object
* @param stream Stream that will be used to log any error
* @param is_input Selects if varying is input or output
*
* @return true if verification is positive, false otherwise
**/
bool checkVarying(Program& program, const Variable& variable, std::stringstream& stream, bool is_input)
{
bool result = true;
if (variable.IsBlock())
{
Utils::Interface* interface = variable.m_descriptor.m_interface;
const size_t n_members = interface->m_members.size();
for (size_t i = 0; i < n_members; ++i)
{
const Variable::Descriptor& member = interface->m_members[i];
bool member_result = verifyVarying(program, interface->m_name, member, stream, is_input);
if (false == member_result)
{
result = false;
}
}
}
/*
To query the the location of struct member by glGetProgramResource, we need pass the variable name "gs_fs_output[0].single",
but in original implementation, the test pass the name "Data.single", which can't get any valid result.
struct Data {
dmat2 single;
dmat2 array[1];
};
layout (location = 0) in Data gs_fs_output[1];
*/
else if (variable.IsStruct())
{
Utils::Interface* interface = variable.m_descriptor.m_interface;
const size_t n_members = interface->m_members.size();
std::string structVariable = variable.m_descriptor.m_name;
// If struct variable is an array
if (0 != variable.m_descriptor.m_n_array_elements)
{
for (GLuint i = 0; i < variable.m_descriptor.m_n_array_elements; i++)
{
GLchar buffer[16];
sprintf(buffer, "%d", i);
structVariable.append("[");
structVariable.append(buffer);
structVariable.append("]");
for (size_t j = 0; j < n_members; ++j)
{
const Variable::Descriptor& member = interface->m_members[j];
bool member_result = verifyVarying(program, structVariable, member, stream, is_input);
if (false == member_result)
{
result = false;
}
}
}
}
else
{
for (GLuint i = 0; i < n_members; ++i)
{
const Variable::Descriptor& member = interface->m_members[i];
bool member_result = verifyVarying(program, structVariable, member, stream, is_input);
if (false == member_result)
{
result = false;
}
}
}
}
else
{
result = verifyVarying(program, "", variable.m_descriptor, stream, is_input);
}
return result;
}
/** Query program resource for given variable and verify that everything is as expected
*
* @param program Program object
* @param variable Variable object
* @param stream Stream that will be used to log any error
*
* @return true if verification is positive, false otherwise
**/
bool checkUniform(Program& program, const Utils::Variable& variable, std::stringstream& stream)
{
bool result = true;
if (false == variable.IsBlock())
{
TCU_FAIL("Not implemented");
}
else
{
Utils::Interface* interface = variable.m_descriptor.m_interface;
size_t size = interface->m_members.size();
std::vector<GLuint> indices;
std::vector<const char*> names;
std::vector<std::string> names_str;
std::vector<GLint> offsets;
indices.resize(size);
names.resize(size);
names_str.resize(size);
offsets.resize(size);
for (size_t i = 0; i < size; ++i)
{
indices[i] = 0;
offsets[i] = 0;
const std::string& name =
Utils::Variable::GetReference(interface->m_name, interface->m_members[i], Utils::Variable::BASIC, 0);
if (Utils::Variable::INTERFACE == interface->m_members[i].m_type)
{
const std::string& member_name = Utils::Variable::GetReference(
name, interface->m_members[i].m_interface->m_members[0], Utils::Variable::BASIC, 0);
names_str[i] = member_name;
}
else
{
names_str[i] = name;
}
names[i] = names_str[i].c_str();
}
try
{
program.GetUniformIndices(static_cast<glw::GLsizei>(size), &names[0], &indices[0]);
program.GetActiveUniformsiv(static_cast<glw::GLsizei>(size), &indices[0], GL_UNIFORM_OFFSET, &offsets[0]);
}
catch (std::exception& exc)
{
stream << "Failed to query program for uniforms in block: " << variable.m_descriptor.m_name
<< ". Reason: " << exc.what() << "\n";
return false;
}
for (size_t i = 0; i < size; ++i)
{
Utils::Variable::Descriptor& desc = interface->m_members[i];
if (offsets[i] != (GLint)desc.m_offset)
{
stream << "Uniform: " << desc.m_name << " - invalid offset: " << offsets[i]
<< " expected: " << desc.m_offset << std::endl;
result = false;
}
}
}
return result;
}
/** Query program resource for given variable and verify that everything is as expected
*
* @param program Program object
* @param variable Variable object
* @param stream Stream that will be used to log any error
*
* @return true if verification is positive, false otherwise
**/
bool checkSSB(Program& program, const Utils::Variable& variable, std::stringstream& stream)
{
bool result = true;
if (false == variable.IsBlock())
{
TCU_FAIL("Not implemented");
}
else
{
Utils::Interface* interface = variable.m_descriptor.m_interface;
size_t size = interface->m_members.size();
for (size_t i = 0; i < size; ++i)
{
GLuint index = 0;
std::string name_str = "";
GLint offset = 0;
const std::string& name =
Utils::Variable::GetReference(interface->m_name, interface->m_members[i], Utils::Variable::BASIC, 0);
if (Utils::Variable::INTERFACE == interface->m_members[i].m_type)
{
const std::string& member_name = Utils::Variable::GetReference(
name, interface->m_members[i].m_interface->m_members[0], Utils::Variable::BASIC, 0);
name_str = member_name;
}
else
{
name_str = name;
}
try
{
index = program.GetResourceIndex(name_str, GL_BUFFER_VARIABLE);
program.GetResource(GL_BUFFER_VARIABLE, index, GL_OFFSET, 1, &offset);
}
catch (std::exception& exc)
{
stream << "Failed to query program for buffer variable: " << variable.m_descriptor.m_name
<< ". Reason: " << exc.what() << "\n";
return false;
}
Utils::Variable::Descriptor& desc = interface->m_members[i];
if (offset != (GLint)desc.m_offset)
{
stream << "Uniform: " << desc.m_name << " - invalid offset: " << offset
<< " expected: " << desc.m_offset << std::endl;
result = false;
}
}
}
return result;
}
/** Query program resources at given stage and verifies results
*
* @param program Program object
* @param program_interface Definition of program interface
* @param stage Stage to be verified
* @param check_inputs Select if inputs should be verified
* @param check_outputs Select if output should be verified
* @param check_uniforms Select if uniforms should be verified
* @param check_ssbs Select if buffers should be verified
* @param stream Stream that will be used to log any error
*
* @return true if verification is positive, false otherwise
**/
bool checkProgramStage(Program& program, const ProgramInterface& program_interface, Utils::Shader::STAGES stage,
bool check_inputs, bool check_outputs, bool check_uniforms, bool check_ssbs,
std::stringstream& stream)
{
typedef Variable::PtrVector::const_iterator const_iterator;
const ShaderInterface& interface = program_interface.GetShaderInterface(stage);
bool result = true;
/* Inputs */
if (true == check_inputs)
{
const Variable::PtrVector& inputs = interface.m_inputs;
for (const_iterator it = inputs.begin(); it != inputs.end(); ++it)
{
if (false == checkVarying(program, **it, stream, true))
{
result = false;
}
}
}
/* Outputs */
if (true == check_outputs)
{
const Variable::PtrVector& outputs = interface.m_outputs;
for (const_iterator it = outputs.begin(); it != outputs.end(); ++it)
{
if (false == checkVarying(program, **it, stream, false))
{
result = false;
}
}
}
/* Uniforms */
if (true == check_uniforms)
{
const Variable::PtrVector& uniforms = interface.m_uniforms;
for (const_iterator it = uniforms.begin(); it != uniforms.end(); ++it)
{
if (false == checkUniform(program, **it, stream))
{
result = false;
}
}
}
/* SSBs */
if (true == check_ssbs)
{
const Variable::PtrVector& ssbs = interface.m_ssb_blocks;
for (const_iterator it = ssbs.begin(); it != ssbs.end(); ++it)
{
if (false == checkSSB(program, **it, stream))
{
result = false;
}
}
}
return result;
}
/** Query resources of monolithic compute program and verifies results
*
* @param program Program object
* @param program_interface Definition of program interface
* @param stream Stream that will be used to log any error
*
* @return true if verification is positive, false otherwise
**/
bool checkMonolithicComputeProgramInterface(Program& program, const ProgramInterface& program_interface,
std::stringstream& stream)
{
bool result = true;
if (false == checkProgramStage(program, program_interface, Shader::COMPUTE, false, false, true, true, stream))
{
result = false;
}
/* Done */
return result;
}
/** Query resources of monolithic draw program and verifies results
*
* @param program Program object
* @param program_interface Definition of program interface
* @param stream Stream that will be used to log any error
*
* @return true if verification is positive, false otherwise
**/
bool checkMonolithicDrawProgramInterface(Program& program, const ProgramInterface& program_interface,
std::stringstream& stream)
{
bool result = true;
if (false == checkProgramStage(program, program_interface, Shader::VERTEX, true, false, true, true, stream))
{
result = false;
}
/* Done */
return result;
}
/** Query resources of separable draw program and verifies results
*
* @param program Program object
* @param program_interface Definition of program interface
* @param stream Stream that will be used to log any error
*
* @return true if verification is positive, false otherwise
**/
bool checkSeparableDrawProgramInterface(Program& program, const ProgramInterface& program_interface,
Utils::Shader::STAGES stage, std::stringstream& stream)
{
bool result = true;
if (false == checkProgramStage(program, program_interface, stage, true, true, true, true, stream))
{
result = false;
}
/* Done */
return result;
}
/** Check if extension is supported
*
* @param context Test context
* @param extension_name Name of extension
*
* @return true if extension is supported, false otherwise
**/
bool isExtensionSupported(deqp::Context& context, const GLchar* extension_name)
{
const std::vector<std::string>& extensions = context.getContextInfo().getExtensions();
if (std::find(extensions.begin(), extensions.end(), extension_name) == extensions.end())
{
return false;
}
return true;
}
/** Check if GL context meets version requirements
*
* @param gl Functions
* @param required_major Minimum required MAJOR_VERSION
* @param required_minor Minimum required MINOR_VERSION
*
* @return true if GL context version is at least as requested, false otherwise
**/
bool isGLVersionAtLeast(const Functions& gl, GLint required_major, GLint required_minor)
{
glw::GLint major = 0;
glw::GLint minor = 0;
gl.getIntegerv(GL_MAJOR_VERSION, &major);
gl.getIntegerv(GL_MINOR_VERSION, &minor);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetIntegerv");
if (major > required_major)
{
/* Major is higher than required one */
return true;
}
else if (major == required_major)
{
if (minor >= required_minor)
{
/* Major is equal to required one */
/* Minor is higher than or equal to required one */
return true;
}
else
{
/* Major is equal to required one */
/* Minor is lower than required one */
return false;
}
}
else
{
/* Major is lower than required one */
return false;
}
}
/** Replace first occurance of <token> with <text> in <string> starting at <search_posistion>
*
* @param token Token string
* @param search_position Position at which find will start, it is updated to position at which replaced text ends
* @param text String that will be used as replacement for <token>
* @param string String to work on
**/
void replaceToken(const GLchar* token, size_t& search_position, const GLchar* text, std::string& string)
{
const size_t text_length = strlen(text);
const size_t token_length = strlen(token);
const size_t token_position = string.find(token, search_position);
#if DEBUG_REPLACE_TOKEN
if (std::string::npos == token_position)
{
string.append("\n\nInvalid token: ");
string.append(token);
TCU_FAIL(string.c_str());
}
#endif /* DEBUG_REPLACE_TOKEN */
string.replace(token_position, token_length, text, text_length);
search_position = token_position + text_length;
}
/** Replace all occurances of <token> with <text> in <string>
*
* @param token Token string
* @param text String that will be used as replacement for <token>
* @param string String to work on
**/
void replaceAllTokens(const GLchar* token, const GLchar* text, std::string& string)
{
const size_t text_length = strlen(text);
const size_t token_length = strlen(token);
size_t search_position = 0;
while (1)
{
const size_t token_position = string.find(token, search_position);
if (std::string::npos == token_position)
{
break;
}
search_position = token_position + text_length;
string.replace(token_position, token_length, text, text_length);
}
}
/** Rounds up the value to the next power of 2.
* This routine does not work for 0, see the url for explanations.
*
* @param value Starting point
*
* @return Calculated value
**/
glw::GLuint roundUpToPowerOf2(glw::GLuint value)
{
/* Taken from: graphics.stanford.edu/~seander/bithacks.html */
--value;
value |= value >> 1;
value |= value >> 2;
value |= value >> 4;
value |= value >> 8;
value |= value >> 16;
++value;
return value;
}
/** Insert elements of list into string.
* List in string is represented either by token "LIST" or "SEPARATORLIST".
* If SEPARATORLIST is available, than SEPARATOR is replaced with <separator>.
* LIST is replaced with <element>SEPARATORLIST
*
* @param element Element to be inserted
* @param separator Separator inserted between elements
* @param search_position Position in string, where search for list should start
* @param string String
**/
void insertElementOfList(const GLchar* element, const GLchar* separator, size_t& search_position, std::string& string)
{
static const char* list = g_list;
static const char* sep_list = "SEPARATORLIST";
/* Try to get "list" positions */
const size_t list_position = string.find(list, search_position);
const size_t sep_list_position = string.find(sep_list, search_position);
/* There is no list in string */
if (std::string::npos == list_position)
{
return;
}
if (9 /* strlen(SEPARATOR) */ == list_position - sep_list_position)
{
replaceToken("SEPARATOR", search_position, separator, string);
}
/* Save search_position */
const size_t start_position = search_position;
/* Prepare new element */
replaceToken("LIST", search_position, "ELEMENTSEPARATORLIST", string);
/* Restore search_position */
search_position = start_position;
/* Replace element and separator */
replaceToken("ELEMENT", search_position, element, string);
}
/** Close list in string.
* If SEPARATORLIST is available, than SEPARATOR is replaced with <separator>
* LIST is replaced with ""
*
* @param separator Separator inserted between elements
* @param search_position Position in string, where search for list should start
* @param string String
**/
void endList(const glw::GLchar* separator, size_t& search_position, std::string& string)
{
const size_t sep_position = string.find("SEPARATOR", search_position);
if (std::string::npos != sep_position)
{
replaceToken("SEPARATOR", search_position, separator, string);
}
replaceToken("LIST", search_position, "", string);
}
/* Buffer constants */
const GLuint Buffer::m_invalid_id = -1;
/** Constructor.
*
* @param context CTS context.
**/
Buffer::Buffer(deqp::Context& context) : m_id(m_invalid_id), m_buffer(Array), m_context(context)
{
}
/** Destructor
*
**/
Buffer::~Buffer()
{
Release();
}
/** Initialize buffer instance
*
* @param buffer Buffer type
* @param usage Buffer usage enum
* @param size <size> parameter
* @param data <data> parameter
**/
void Buffer::Init(BUFFERS buffer, USAGE usage, GLsizeiptr size, GLvoid* data)
{
/* Delete previous buffer instance */
Release();
m_buffer = buffer;
const Functions& gl = m_context.getRenderContext().getFunctions();
Generate(gl, m_id);
Bind(gl, m_id, m_buffer);
Data(gl, m_buffer, usage, size, data);
}
/** Release buffer instance
*
**/
void Buffer::Release()
{
if (m_invalid_id != m_id)
{
const Functions& gl = m_context.getRenderContext().getFunctions();
gl.deleteBuffers(1, &m_id);
m_id = m_invalid_id;
}
}
/** Binds buffer to its target
*
**/
void Buffer::Bind() const
{
const Functions& gl = m_context.getRenderContext().getFunctions();
Bind(gl, m_id, m_buffer);
}
/** Binds indexed buffer
*
* @param index <index> parameter
**/
void Buffer::BindBase(GLuint index) const
{
const Functions& gl = m_context.getRenderContext().getFunctions();
BindBase(gl, m_id, m_buffer, index);
}
/** Binds range of buffer
*
* @param index <index> parameter
* @param offset <offset> parameter
* @param size <size> parameter
**/
void Buffer::BindRange(GLuint index, GLintptr offset, GLsizeiptr size) const
{
const Functions& gl = m_context.getRenderContext().getFunctions();
BindRange(gl, m_id, m_buffer, index, offset, size);
}
/** Allocate memory for buffer and sends initial content
*
* @param usage Buffer usage enum
* @param size <size> parameter
* @param data <data> parameter
**/
void Buffer::Data(USAGE usage, glw::GLsizeiptr size, glw::GLvoid* data)
{
const Functions& gl = m_context.getRenderContext().getFunctions();
Data(gl, m_buffer, usage, size, data);
}
/** Maps contents of buffer into CPU space
*
* @param access Requested access
*
* @return Pointer to memory region available for CPU
**/
GLvoid* Buffer::Map(ACCESS access)
{
const Functions& gl = m_context.getRenderContext().getFunctions();
return Map(gl, m_buffer, access);
}
/** Allocate memory for buffer and sends initial content
*
* @param offset Offset in buffer
* @param size <size> parameter
* @param data <data> parameter
**/
void Buffer::SubData(glw::GLintptr offset, glw::GLsizeiptr size, glw::GLvoid* data)
{
const Functions& gl = m_context.getRenderContext().getFunctions();
SubData(gl, m_buffer, offset, size, data);
}
/** Maps contents of buffer into CPU space
**/
void Buffer::UnMap()
{
const Functions& gl = m_context.getRenderContext().getFunctions();
return UnMap(gl, m_buffer);
}
/** Bind buffer to given target
*
* @param gl GL functions
* @param id Id of buffer
* @param buffer Buffer enum
**/
void Buffer::Bind(const Functions& gl, GLuint id, BUFFERS buffer)
{
GLenum target = GetBufferGLenum(buffer);
gl.bindBuffer(target, id);
GLU_EXPECT_NO_ERROR(gl.getError(), "BindBuffer");
}
/** Binds indexed buffer
*
* @param gl GL functions
* @param id Id of buffer
* @param buffer Buffer enum
* @param index <index> parameter
**/
void Buffer::BindBase(const Functions& gl, GLuint id, BUFFERS buffer, GLuint index)
{
GLenum target = GetBufferGLenum(buffer);
gl.bindBufferBase(target, index, id);
GLU_EXPECT_NO_ERROR(gl.getError(), "BindBufferBase");
}
/** Binds buffer range
*
* @param gl GL functions
* @param id Id of buffer
* @param buffer Buffer enum
* @param index <index> parameter
* @param offset <offset> parameter
* @param size <size> parameter
**/
void Buffer::BindRange(const Functions& gl, GLuint id, BUFFERS buffer, GLuint index, GLintptr offset, GLsizeiptr size)
{
GLenum target = GetBufferGLenum(buffer);
gl.bindBufferRange(target, index, id, offset, size);
GLU_EXPECT_NO_ERROR(gl.getError(), "BindBufferRange");
}
/** Allocate memory for buffer and sends initial content
*
* @param gl GL functions
* @param buffer Buffer enum
* @param usage Buffer usage enum
* @param size <size> parameter
* @param data <data> parameter
**/
void Buffer::Data(const glw::Functions& gl, BUFFERS buffer, USAGE usage, glw::GLsizeiptr size, glw::GLvoid* data)
{
GLenum target = GetBufferGLenum(buffer);
GLenum gl_usage = GetUsageGLenum(usage);
gl.bufferData(target, size, data, gl_usage);
GLU_EXPECT_NO_ERROR(gl.getError(), "BufferData");
}
/** Allocate memory for buffer and sends initial content
*
* @param gl GL functions
* @param buffer Buffer enum
* @param offset Offset in buffer
* @param size <size> parameter
* @param data <data> parameter
**/
void Buffer::SubData(const glw::Functions& gl, BUFFERS buffer, glw::GLintptr offset, glw::GLsizeiptr size,
glw::GLvoid* data)
{
GLenum target = GetBufferGLenum(buffer);
gl.bufferSubData(target, offset, size, data);
GLU_EXPECT_NO_ERROR(gl.getError(), "BufferSubData");
}
/** Generate buffer
*
* @param gl GL functions
* @param out_id Id of buffer
**/
void Buffer::Generate(const Functions& gl, GLuint& out_id)
{
GLuint id = m_invalid_id;
gl.genBuffers(1, &id);
GLU_EXPECT_NO_ERROR(gl.getError(), "GenBuffers");
if (m_invalid_id == id)
{
TCU_FAIL("Got invalid id");
}
out_id = id;
}
/** Maps buffer content
*
* @param gl GL functions
* @param buffer Buffer enum
* @param access Access rights for mapped region
*
* @return Mapped memory
**/
void* Buffer::Map(const Functions& gl, BUFFERS buffer, ACCESS access)
{
GLenum target = GetBufferGLenum(buffer);
GLenum gl_access = GetAccessGLenum(access);
void* result = gl.mapBuffer(target, gl_access);
GLU_EXPECT_NO_ERROR(gl.getError(), "MapBuffer");
return result;
}
/** Unmaps buffer
*
**/
void Buffer::UnMap(const Functions& gl, BUFFERS buffer)
{
GLenum target = GetBufferGLenum(buffer);
gl.unmapBuffer(target);
GLU_EXPECT_NO_ERROR(gl.getError(), "UnmapBuffer");
}
/** Return GLenum representation of requested access
*
* @param access Requested access
*
* @return GLenum value
**/
GLenum Buffer::GetAccessGLenum(ACCESS access)
{
GLenum result = 0;
switch (access)
{
case ReadOnly:
result = GL_READ_ONLY;
break;
case WriteOnly:
result = GL_WRITE_ONLY;
break;
case ReadWrite:
result = GL_READ_WRITE;
break;
default:
TCU_FAIL("Invalid enum");
}
return result;
}
/** Return GLenum representation of requested buffer type
*
* @param buffer Requested buffer type
*
* @return GLenum value
**/
GLenum Buffer::GetBufferGLenum(BUFFERS buffer)
{
GLenum result = 0;
switch (buffer)
{
case Array:
result = GL_ARRAY_BUFFER;
break;
case Element:
result = GL_ELEMENT_ARRAY_BUFFER;
break;
case Shader_Storage:
result = GL_SHADER_STORAGE_BUFFER;
break;
case Texture:
result = GL_TEXTURE_BUFFER;
break;
case Transform_feedback:
result = GL_TRANSFORM_FEEDBACK_BUFFER;
break;
case Uniform:
result = GL_UNIFORM_BUFFER;
break;
default:
TCU_FAIL("Invalid enum");
}
return result;
}
/** Return GLenum representation of requested usage
*
* @param usage Requested usage
*
* @return GLenum value
**/
GLenum Buffer::GetUsageGLenum(USAGE usage)
{
GLenum result = 0;
switch (usage)
{
case DynamicCopy:
result = GL_DYNAMIC_COPY;
break;
case DynamicDraw:
result = GL_DYNAMIC_DRAW;
break;
case DynamicRead:
result = GL_DYNAMIC_READ;
break;
case StaticCopy:
result = GL_STATIC_COPY;
break;
case StaticDraw:
result = GL_STATIC_DRAW;
break;
case StaticRead:
result = GL_STATIC_READ;
break;
case StreamCopy:
result = GL_STREAM_COPY;
break;
case StreamDraw:
result = GL_STREAM_DRAW;
break;
case StreamRead:
result = GL_STREAM_READ;
break;
default:
TCU_FAIL("Invalid enum");
}
return result;
}
/** Returns name of buffer target
*
* @param buffer Target enum
*
* @return Name of target
**/
const GLchar* Buffer::GetBufferName(BUFFERS buffer)
{
const GLchar* name = 0;
switch (buffer)
{
case Array:
name = "Array";
break;
case Element:
name = "Element";
break;
case Shader_Storage:
name = "Shader_Storage";
break;
case Texture:
name = "Texture";
break;
case Transform_feedback:
name = "Transform_feedback";
break;
case Uniform:
name = "Uniform";
break;
default:
TCU_FAIL("Invalid enum");
}
return name;
}
/* Framebuffer constants */
const GLuint Framebuffer::m_invalid_id = -1;
/** Constructor
*
* @param context CTS context
**/
Framebuffer::Framebuffer(deqp::Context& context) : m_id(m_invalid_id), m_context(context)
{
/* Nothing to be done here */
}
/** Destructor
*
**/
Framebuffer::~Framebuffer()
{
Release();
}
/** Initialize framebuffer instance
*
**/
void Framebuffer::Init()
{
/* Delete previous instance */
Release();
const Functions& gl = m_context.getRenderContext().getFunctions();
Generate(gl, m_id);
}
/** Release framebuffer instance
*
**/
void Framebuffer::Release()
{
if (m_invalid_id != m_id)
{
const Functions& gl = m_context.getRenderContext().getFunctions();
gl.deleteFramebuffers(1, &m_id);
m_id = m_invalid_id;
}
}
/** Attach texture to specified attachment
*
* @param attachment Attachment
* @param texture_id Texture id
* @param width Texture width
* @param height Texture height
**/
void Framebuffer::AttachTexture(GLenum attachment, GLuint texture_id, GLuint width, GLuint height)
{
const Functions& gl = m_context.getRenderContext().getFunctions();
AttachTexture(gl, attachment, texture_id, width, height);
}
/** Binds framebuffer to DRAW_FRAMEBUFFER
*
**/
void Framebuffer::Bind()
{
const Functions& gl = m_context.getRenderContext().getFunctions();
Bind(gl, m_id);
}
/** Clear framebuffer
*
* @param mask <mask> parameter of glClear. Decides which shall be cleared
**/
void Framebuffer::Clear(GLenum mask)
{
const Functions& gl = m_context.getRenderContext().getFunctions();
Clear(gl, mask);
}
/** Specifies clear color
*
* @param red Red channel
* @param green Green channel
* @param blue Blue channel
* @param alpha Alpha channel
**/
void Framebuffer::ClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)
{
const Functions& gl = m_context.getRenderContext().getFunctions();
ClearColor(gl, red, green, blue, alpha);
}
/** Attach texture to specified attachment
*
* @param gl GL functions
* @param attachment Attachment
* @param texture_id Texture id
* @param width Texture width
* @param height Texture height
**/
void Framebuffer::AttachTexture(const Functions& gl, GLenum attachment, GLuint texture_id, GLuint width, GLuint height)
{
gl.framebufferTexture(GL_DRAW_FRAMEBUFFER, attachment, texture_id, 0 /* level */);
GLU_EXPECT_NO_ERROR(gl.getError(), "FramebufferTexture");
gl.viewport(0 /* x */, 0 /* y */, width, height);
GLU_EXPECT_NO_ERROR(gl.getError(), "Viewport");
}
/** Binds framebuffer to DRAW_FRAMEBUFFER
*
* @param gl GL functions
* @param id ID of framebuffer
**/
void Framebuffer::Bind(const Functions& gl, GLuint id)
{
gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, id);
GLU_EXPECT_NO_ERROR(gl.getError(), "BindFramebuffer");
}
/** Clear framebuffer
*
* @param gl GL functions
* @param mask <mask> parameter of glClear. Decides which shall be cleared
**/
void Framebuffer::Clear(const Functions& gl, GLenum mask)
{
gl.clear(mask);
GLU_EXPECT_NO_ERROR(gl.getError(), "Clear");
}
/** Specifies clear color
*
* @param gl GL functions
* @param red Red channel
* @param green Green channel
* @param blue Blue channel
* @param alpha Alpha channel
**/
void Framebuffer::ClearColor(const Functions& gl, GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)
{
gl.clearColor(red, green, blue, alpha);
GLU_EXPECT_NO_ERROR(gl.getError(), "ClearColor");
}
/** Generate framebuffer
*
**/
void Framebuffer::Generate(const Functions& gl, GLuint& out_id)
{
GLuint id = m_invalid_id;
gl.genFramebuffers(1, &id);
GLU_EXPECT_NO_ERROR(gl.getError(), "GenFramebuffers");
if (m_invalid_id == id)
{
TCU_FAIL("Invalid id");
}
out_id = id;
}
/* Shader's constants */
const GLuint Shader::m_invalid_id = 0;
/** Constructor.
*
* @param context CTS context.
**/
Shader::Shader(deqp::Context& context) : m_id(m_invalid_id), m_context(context)
{
/* Nothing to be done here */
}
/** Destructor
*
**/
Shader::~Shader()
{
Release();
}
/** Initialize shader instance
*
* @param stage Shader stage
* @param source Source code
**/
void Shader::Init(STAGES stage, const std::string& source)
{
if (true == source.empty())
{
/* No source == no shader */
return;
}
/* Delete any previous shader */
Release();
/* Create, set source and compile */
const Functions& gl = m_context.getRenderContext().getFunctions();
Create(gl, stage, m_id);
Source(gl, m_id, source);
try
{
Compile(gl, m_id);
}
catch (const CompilationException& exc)
{
throw InvalidSourceException(exc.what(), source, stage);
}
}
/** Release shader instance
*
**/
void Shader::Release()
{
if (m_invalid_id != m_id)
{
const Functions& gl = m_context.getRenderContext().getFunctions();
gl.deleteShader(m_id);
m_id = m_invalid_id;
}
}
/** Compile shader
*
* @param gl GL functions
* @param id Shader id
**/
void Shader::Compile(const Functions& gl, GLuint id)
{
GLint status = GL_FALSE;
/* Compile */
gl.compileShader(id);
GLU_EXPECT_NO_ERROR(gl.getError(), "CompileShader");
/* Get compilation status */
gl.getShaderiv(id, GL_COMPILE_STATUS, &status);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetShaderiv");
/* Log compilation error */
if (GL_TRUE != status)
{
glw::GLint length = 0;
std::string message;
/* Error log length */
gl.getShaderiv(id, GL_INFO_LOG_LENGTH, &length);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetShaderiv");
/* Prepare storage */
message.resize(length, 0);
/* Get error log */
gl.getShaderInfoLog(id, length, 0, &message[0]);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetShaderInfoLog");
throw CompilationException(message.c_str());
}
}
/** Create shader
*
* @param gl GL functions
* @param stage Shader stage
* @param out_id Shader id
**/
void Shader::Create(const Functions& gl, STAGES stage, GLuint& out_id)
{
const GLenum shaderType = GetShaderStageGLenum(stage);
const GLuint id = gl.createShader(shaderType);
GLU_EXPECT_NO_ERROR(gl.getError(), "CreateShader");
if (m_invalid_id == id)
{
TCU_FAIL("Failed to create shader");
}
out_id = id;
}
/** Set shader's source code
*
* @param gl GL functions
* @param id Shader id
* @param source Shader source code
**/
void Shader::Source(const Functions& gl, GLuint id, const std::string& source)
{
const GLchar* code = source.c_str();
gl.shaderSource(id, 1 /* count */, &code, 0 /* lengths */);
GLU_EXPECT_NO_ERROR(gl.getError(), "ShaderSource");
}
/** Get GLenum repesenting shader stage
*
* @param stage Shader stage
*
* @return GLenum
**/
GLenum Shader::GetShaderStageGLenum(STAGES stage)
{
GLenum result = 0;
switch (stage)
{
case COMPUTE:
result = GL_COMPUTE_SHADER;
break;
case FRAGMENT:
result = GL_FRAGMENT_SHADER;
break;
case GEOMETRY:
result = GL_GEOMETRY_SHADER;
break;
case TESS_CTRL:
result = GL_TESS_CONTROL_SHADER;
break;
case TESS_EVAL:
result = GL_TESS_EVALUATION_SHADER;
break;
case VERTEX:
result = GL_VERTEX_SHADER;
break;
default:
TCU_FAIL("Invalid enum");
}
return result;
}
/** Get string representing name of shader stage
*
* @param stage Shader stage
*
* @return String with name of shader stage
**/
const glw::GLchar* Shader::GetStageName(STAGES stage)
{
const GLchar* result = 0;
switch (stage)
{
case COMPUTE:
result = "compute";
break;
case VERTEX:
result = "vertex";
break;
case TESS_CTRL:
result = "tesselation control";
break;
case TESS_EVAL:
result = "tesselation evaluation";
break;
case GEOMETRY:
result = "geomtery";
break;
case FRAGMENT:
result = "fragment";
break;
default:
TCU_FAIL("Invalid enum");
}
return result;
}
/** Logs shader source
*
* @param context CTS context
* @param source Source of shader
* @param stage Shader stage
**/
void Shader::LogSource(deqp::Context& context, const std::string& source, STAGES stage)
{
/* Skip empty shaders */
if (true == source.empty())
{
return;
}
context.getTestContext().getLog() << tcu::TestLog::Message
<< "Shader source. Stage: " << Shader::GetStageName(stage)
<< tcu::TestLog::EndMessage << tcu::TestLog::KernelSource(source);
}
/** Constructor
*
* @param message Compilation error message
**/
Shader::CompilationException::CompilationException(const GLchar* message)
{
m_message = message;
}
/** Returns error messages
*
* @return Compilation error message
**/
const char* Shader::CompilationException::what() const throw()
{
return m_message.c_str();
}
/** Constructor
*
* @param message Compilation error message
**/
Shader::InvalidSourceException::InvalidSourceException(const GLchar* error_message, const std::string& source,
STAGES stage)
: m_message(error_message), m_source(source), m_stage(stage)
{
}
/** Returns error messages
*
* @return Compilation error message
**/
const char* Shader::InvalidSourceException::what() const throw()
{
return "Compilation error";
}
/** Logs error message and shader sources **/
void Shader::InvalidSourceException::log(deqp::Context& context) const
{
context.getTestContext().getLog() << tcu::TestLog::Message << "Failed to compile shader: " << m_message.c_str()
<< tcu::TestLog::EndMessage;
LogSource(context, m_source, m_stage);
}
/* Program constants */
const GLuint Pipeline::m_invalid_id = 0;
/** Constructor.
*
* @param context CTS context.
**/
Pipeline::Pipeline(deqp::Context& context) : m_id(m_invalid_id), m_context(context)
{
/* Nothing to be done here */
}
/** Destructor
*
**/
Pipeline::~Pipeline()
{
Release();
}
/** Initialize pipline object
*
**/
void Pipeline::Init()
{
Release();
const Functions& gl = m_context.getRenderContext().getFunctions();
/* Generate */
gl.genProgramPipelines(1, &m_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "GenProgramPipelines");
}
/** Release pipeline object
*
**/
void Pipeline::Release()
{
if (m_invalid_id != m_id)
{
const Functions& gl = m_context.getRenderContext().getFunctions();
/* Generate */
gl.deleteProgramPipelines(1, &m_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "DeleteProgramPipelines");
m_id = m_invalid_id;
}
}
/** Bind pipeline
*
**/
void Pipeline::Bind()
{
const Functions& gl = m_context.getRenderContext().getFunctions();
Bind(gl, m_id);
}
/** Set which stages should be active
*
* @param program_id Id of program
* @param stages Logical combination of enums representing stages
**/
void Pipeline::UseProgramStages(GLuint program_id, GLenum stages)
{
const Functions& gl = m_context.getRenderContext().getFunctions();
UseProgramStages(gl, m_id, program_id, stages);
}
/** Bind pipeline
*
* @param gl Functiions
* @param id Pipeline id
**/
void Pipeline::Bind(const Functions& gl, GLuint id)
{
gl.bindProgramPipeline(id);
GLU_EXPECT_NO_ERROR(gl.getError(), "BindProgramPipeline");
}
/** Set which stages should be active
*
* @param gl Functiions
* @param id Pipeline id
* @param program_id Id of program
* @param stages Logical combination of enums representing stages
**/
void Pipeline::UseProgramStages(const Functions& gl, GLuint id, GLuint program_id, GLenum stages)
{
gl.useProgramStages(id, stages, program_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "UseProgramStages");
}
/* Program constants */
const GLuint Program::m_invalid_id = 0;
/** Constructor.
*
* @param context CTS context.
**/
Program::Program(deqp::Context& context)
: m_id(m_invalid_id)
, m_compute(context)
, m_fragment(context)
, m_geometry(context)
, m_tess_ctrl(context)
, m_tess_eval(context)
, m_vertex(context)
, m_context(context)
{
/* Nothing to be done here */
}
/** Destructor
*
**/
Program::~Program()
{
Release();
}
/** Initialize program instance
*
* @param compute_shader Compute shader source code
* @param fragment_shader Fragment shader source code
* @param geometry_shader Geometry shader source code
* @param tesselation_control_shader Tesselation control shader source code
* @param tesselation_evaluation_shader Tesselation evaluation shader source code
* @param vertex_shader Vertex shader source code
* @param captured_varyings Vector of variables to be captured with transfrom feedback
* @param capture_interleaved Select mode of transform feedback (separate or interleaved)
* @param is_separable Selects if monolithic or separable program should be built. Defaults to false
**/
void Program::Init(const std::string& compute_shader, const std::string& fragment_shader,
const std::string& geometry_shader, const std::string& tesselation_control_shader,
const std::string& tesselation_evaluation_shader, const std::string& vertex_shader,
const NameVector& captured_varyings, bool capture_interleaved, bool is_separable)
{
/* Delete previous program */
Release();
/* GL entry points */
const Functions& gl = m_context.getRenderContext().getFunctions();
/* Initialize shaders */
m_compute.Init(Shader::COMPUTE, compute_shader);
m_fragment.Init(Shader::FRAGMENT, fragment_shader);
m_geometry.Init(Shader::GEOMETRY, geometry_shader);
m_tess_ctrl.Init(Shader::TESS_CTRL, tesselation_control_shader);
m_tess_eval.Init(Shader::TESS_EVAL, tesselation_evaluation_shader);
m_vertex.Init(Shader::VERTEX, vertex_shader);
/* Create program, set up transform feedback and attach shaders */
Create(gl, m_id);
Capture(gl, m_id, captured_varyings, capture_interleaved);
Attach(gl, m_id, m_compute.m_id);
Attach(gl, m_id, m_fragment.m_id);
Attach(gl, m_id, m_geometry.m_id);
Attach(gl, m_id, m_tess_ctrl.m_id);
Attach(gl, m_id, m_tess_eval.m_id);
Attach(gl, m_id, m_vertex.m_id);
/* Set separable parameter */
if (true == is_separable)
{
gl.programParameteri(m_id, GL_PROGRAM_SEPARABLE, GL_TRUE);
GLU_EXPECT_NO_ERROR(gl.getError(), "ProgramParameteri");
}
try
{
/* Link program */
Link(gl, m_id);
}
catch (const LinkageException& exc)
{
throw BuildException(exc.what(), compute_shader, fragment_shader, geometry_shader, tesselation_control_shader,
tesselation_evaluation_shader, vertex_shader);
}
}
/** Initialize program instance
*
* @param compute_shader Compute shader source code
* @param fragment_shader Fragment shader source code
* @param geometry_shader Geometry shader source code
* @param tesselation_control_shader Tesselation control shader source code
* @param tesselation_evaluation_shader Tesselation evaluation shader source code
* @param vertex_shader Vertex shader source code
* @param is_separable Selects if monolithic or separable program should be built. Defaults to false
**/
void Program::Init(const std::string& compute_shader, const std::string& fragment_shader,
const std::string& geometry_shader, const std::string& tesselation_control_shader,
const std::string& tesselation_evaluation_shader, const std::string& vertex_shader,
bool is_separable)
{
NameVector captured_varying;
Init(compute_shader, fragment_shader, geometry_shader, tesselation_control_shader, tesselation_evaluation_shader,
vertex_shader, captured_varying, true, is_separable);
}
/** Release program instance
*
**/
void Program::Release()
{
const Functions& gl = m_context.getRenderContext().getFunctions();
if (m_invalid_id != m_id)
{
Use(gl, m_invalid_id);
gl.deleteProgram(m_id);
m_id = m_invalid_id;
}
m_compute.Release();
m_fragment.Release();
m_geometry.Release();
m_tess_ctrl.Release();
m_tess_eval.Release();
m_vertex.Release();
}
/** Get <pname> for a set of active uniforms
*
* @param count Number of indices
* @param indices Indices of uniforms
* @param pname Queired pname
* @param params Array that will be filled with values of parameters
**/
void Program::GetActiveUniformsiv(GLsizei count, const GLuint* indices, GLenum pname, GLint* params) const
{
const Functions& gl = m_context.getRenderContext().getFunctions();
GetActiveUniformsiv(gl, m_id, count, indices, pname, params);
}
/** Get location of attribute
*
* @param name Name of attribute
*
* @return Result of query
**/
glw::GLint Program::GetAttribLocation(const std::string& name) const
{
const Functions& gl = m_context.getRenderContext().getFunctions();
return GetAttribLocation(gl, m_id, name);
}
/** Query resource
*
* @param interface Interface to be queried
* @param index Index of resource
* @param property Property to be queried
* @param buf_size Size of <params> buffer
* @param params Results of query
**/
void Program::GetResource(GLenum interface, GLuint index, GLenum property, GLsizei buf_size, GLint* params) const
{
const Functions& gl = m_context.getRenderContext().getFunctions();
GetResource(gl, m_id, interface, index, property, buf_size, params);
}
/** Query for index of resource
*
* @param name Name of resource
* @param interface Interface to be queried
*
* @return Result of query
**/
glw::GLuint Program::GetResourceIndex(const std::string& name, GLenum interface) const
{
const Functions& gl = m_context.getRenderContext().getFunctions();
return GetResourceIndex(gl, m_id, name, interface);
}
/** Get indices for a set of uniforms
*
* @param count Count number of uniforms
* @param names Names of uniforms
* @param indices Buffer that will be filled with indices
**/
void Program::GetUniformIndices(GLsizei count, const GLchar** names, GLuint* indices) const
{
const Functions& gl = m_context.getRenderContext().getFunctions();
GetUniformIndices(gl, m_id, count, names, indices);
}
/** Get uniform location
*
* @param name Name of uniform
*
* @return Results of query
**/
glw::GLint Program::GetUniformLocation(const std::string& name) const
{
const Functions& gl = m_context.getRenderContext().getFunctions();
return GetUniformLocation(gl, m_id, name);
}
/** Set program as active
*
**/
void Program::Use() const
{
const Functions& gl = m_context.getRenderContext().getFunctions();
Use(gl, m_id);
}
/** Attach shader to program
*
* @param gl GL functions
* @param program_id Id of program
* @param shader_id Id of shader
**/
void Program::Attach(const Functions& gl, GLuint program_id, GLuint shader_id)
{
/* Sanity checks */
if ((m_invalid_id == program_id) || (Shader::m_invalid_id == shader_id))
{
return;
}
gl.attachShader(program_id, shader_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "AttachShader");
}
/** Set up captured varyings
*
* @param gl GL functions
* @param id Id of program
* @param captured_varyings Vector of varyings
* @param capture_interleaved Selects if interleaved or separate mode should be used
**/
void Program::Capture(const Functions& gl, GLuint id, const NameVector& captured_varyings, bool capture_interleaved)
{
const size_t n_varyings = captured_varyings.size();
if (0 == n_varyings)
{
/* empty list, skip */
return;
}
std::vector<const GLchar*> varying_names;
varying_names.resize(n_varyings);
for (size_t i = 0; i < n_varyings; ++i)
{
varying_names[i] = captured_varyings[i].c_str();
}
GLenum mode = 0;
if (true == capture_interleaved)
{
mode = GL_INTERLEAVED_ATTRIBS;
}
else
{
mode = GL_SEPARATE_ATTRIBS;
}
gl.transformFeedbackVaryings(id, static_cast<GLsizei>(n_varyings), &varying_names[0], mode);
GLU_EXPECT_NO_ERROR(gl.getError(), "TransformFeedbackVaryings");
}
/** Create program instance
*
* @param gl GL functions
* @param out_id Id of program
**/
void Program::Create(const Functions& gl, GLuint& out_id)
{
const GLuint id = gl.createProgram();
GLU_EXPECT_NO_ERROR(gl.getError(), "CreateProgram");
if (m_invalid_id == id)
{
TCU_FAIL("Failed to create program");
}
out_id = id;
}
/** Get <pname> for a set of active uniforms
*
* @param gl Functions
* @param program_id Id of program
* @param count Number of indices
* @param indices Indices of uniforms
* @param pname Queired pname
* @param params Array that will be filled with values of parameters
**/
void Program::GetActiveUniformsiv(const Functions& gl, GLuint program_id, GLsizei count, const GLuint* indices,
GLenum pname, GLint* params)
{
gl.getActiveUniformsiv(program_id, count, indices, pname, params);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetActiveUniformsiv");
}
/** Get indices for a set of uniforms
*
* @param gl Functions
* @param program_id Id of program
* @param count Count number of uniforms
* @param names Names of uniforms
* @param indices Buffer that will be filled with indices
**/
void Program::GetUniformIndices(const Functions& gl, GLuint program_id, GLsizei count, const GLchar** names,
GLuint* indices)
{
gl.getUniformIndices(program_id, count, names, indices);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetUniformIndices");
}
/** Link program
*
* @param gl GL functions