| /*------------------------------------------------------------------------- |
| * 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 "gluShaderUtil.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_NEG_REMOVE_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); |
| const GLuint padding = alignment - GetTypeSize(m_basic_type) * m_n_rows; |
| const GLuint data_size = alignment * m_n_columns - padding; |
| |
| std::vector<GLubyte> data; |
| data.resize(data_size); |
| |
| 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("Invalid 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 or if it's a vertex shader input. |
| **/ |
| GLuint Type::GetLocations(bool is_vs_input) const |
| { |
| GLuint n_loc_per_column; |
| |
| /* 1 or 2 doubles any for rest */ |
| if ((2 >= m_n_rows) || (Double != m_basic_type) || is_vs_input) |
| { |
| 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 doesn't consider arrays and assumes |
| * column_major matrices. |
| * |
| * @return Formula: |
| * - If std140 packaging and matrix; number of columns * base alignment |
| * - Otherwise; number of elements * sizeof(base_type) |
| **/ |
| GLuint Type::GetSize(const bool is_std140) const |
| { |
| const GLuint basic_type_size = GetTypeSize(m_basic_type); |
| const GLuint n_elements = m_n_columns * m_n_rows; |
| |
| if (is_std140 && m_n_columns > 1) |
| { |
| return m_n_columns * GetBaseAlignment(false); |
| } |
| |
| 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("Invalid enum"); |
| } |
| |
| return result; |
| } |
| |
| /** Calculate the number 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 the valid values to use with the component qualifier |
| * |
| * @return Vector with the valid values, in growing order, or empty if |
| * the component qualifier is not allowed |
| **/ |
| std::vector<GLuint> Type::GetValidComponents() const |
| { |
| const GLuint component_size = Utils::Type::Double == m_basic_type ? 2 : 1; |
| const GLuint n_components_per_location = Utils::Type::Double == m_basic_type ? 2 : 4; |
| const GLuint n_req_components = m_n_rows; |
| const GLint max_valid_component = (GLint)n_components_per_location - (GLint)n_req_components; |
| std::vector<GLuint> data; |
| |
| /* The component qualifier cannot be used for matrices */ |
| if (1 != m_n_columns) |
| { |
| return data; |
| } |
| |
| /* The component qualifier cannot be used for dvec3/dvec4 */ |
| if (max_valid_component < 0) |
| { |
| return data; |
| } |
| |
| for (GLuint i = 0; i <= (GLuint)max_valid_component; ++i) |
| { |
| data.push_back(i * component_size); |
| } |
| |
| return data; |
| } |
| |
| /** 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("Invalid 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; |
| } |
| |
| /** Check if two types can share the same location, based on the underlying numerical type and bit width |
| * |
| * @param first First type to compare |
| * @param second Second type to compare |
| * |
| * @return true if the types can share the same location |
| **/ |
| bool Type::CanTypesShareLocation(TYPES first, TYPES second) |
| { |
| if (first == second) |
| { |
| return true; |
| } |
| |
| if (Float == first || Float == second || Double == first || Double == second) |
| { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /** 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, Shader::STAGES stage, 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; |
| |
| switch (Variable::GetFlavour(stage, is_input ? Variable::INPUT : Variable::OUTPUT)) |
| { |
| case Variable::ARRAY: |
| case Variable::INDEXED_BY_INVOCATION_ID: |
| structVariable.append("[0]"); |
| break; |
| default: |
| break; |
| } |
| |
| // 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, stage, **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, stage, **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 = "tessellation control"; |
| break; |
| case TESS_EVAL: |
| result = "tessellation evaluation"; |
| break; |
| case GEOMETRY: |
| result = "geometry"; |
| 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 tessellation_control_shader Tessellation control shader source code |
| * @param tessellation_evaluation_shader Tessellation 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& tessellation_control_shader, |
| const std::string& tessellation_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, tessellation_control_shader); |
| m_tess_eval.Init(Shader::TESS_EVAL, tessellation_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, tessellation_control_shader, |
| tessellation_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 tessellation_control_shader Tessellation control shader source code |
| * @param tessellation_evaluation_shader Tessellation 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& tessellation_control_shader, |
| const std::string& tessellation_evaluation_shader, const std::string& vertex_shader, |
| bool is_separable) |
| { |
| NameVector captured_varying; |
| |
| Init(compute_shader, fragment_shader, geometry_shader, tessellation_control_shader, tessellation_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 |
| * @param id Id of program |
| **/ |
| void Program::Link(const Functions& gl, GLuint id) |
| { |
| GLint status = GL_FALSE; |
| |
| gl.linkProgram(id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "LinkProgram"); |
| |
| /* Get link status */ |
| gl.getProgramiv(id, GL_LINK_STATUS, &status); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramiv"); |
| |
| /* Log link error */ |
| if (GL_TRUE != status) |
| { |
| glw::GLint length = 0; |
| std::string message; |
| |
| /* Get error log length */ |
| gl.getProgramiv(id, GL_INFO_LOG_LENGTH, &length); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramiv"); |
| |
| message.resize(length, 0); |
| |
| /* Get error log */ |
| gl.getProgramInfoLog(id, length, 0, &message[0]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramInfoLog"); |
| |
| throw LinkageException(message.c_str()); |
| } |
| } |
| |
| /** Set generic uniform |
| * |
| * @param gl Functions |
| * @param type Type of uniform |
| * @param count Length of array |
| * @param location Location of uniform |
| * @param data Data that will be used |
| **/ |
| void Program::Uniform(const Functions& gl, const Type& type, GLsizei count, GLint location, const GLvoid* data) |
| { |
| if (-1 == location) |
| { |
| TCU_FAIL("Uniform is inactive"); |
| } |
| |
| switch (type.m_basic_type) |
| { |
| case Type::Double: |
| if (1 == type.m_n_columns) |
| { |
| getUniformNdv(gl, type.m_n_rows)(location, count, (const GLdouble*)data); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "UniformNdv"); |
| } |
| else |
| { |
| getUniformMatrixNdv(gl, type.m_n_columns, type.m_n_rows)(location, count, false, (const GLdouble*)data); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "UniformMatrixNdv"); |
| } |
| break; |
| case Type::Float: |
| if (1 == type.m_n_columns) |
| { |
| getUniformNfv(gl, type.m_n_rows)(location, count, (const GLfloat*)data); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "UniformNfv"); |
| } |
| else |
| { |
| getUniformMatrixNfv(gl, type.m_n_columns, type.m_n_rows)(location, count, false, (const GLfloat*)data); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "UniformMatrixNfv"); |
| } |
| break; |
| case Type::Int: |
| getUniformNiv(gl, type.m_n_rows)(location, count, (const GLint*)data); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "UniformNiv"); |
| break; |
| case Type::Uint: |
| getUniformNuiv(gl, type.m_n_rows)(location, count, (const GLuint*)data); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "UniformNuiv"); |
| break; |
| default: |
| TCU_FAIL("Invalid enum"); |
| } |
| } |
| |
| /** Use program |
| * |
| * @param gl GL functions |
| * @param id Id of program |
| **/ |
| void Program::Use(const Functions& gl, GLuint id) |
| { |
| gl.useProgram(id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "UseProgram"); |
| } |
| |
| /** Get location of attribute |
| * |
| * @param gl GL functions |
| * @param id Id of program |
| * @param name Name of attribute |
| * |
| * @return Location of attribute |
| **/ |
| GLint Program::GetAttribLocation(const Functions& gl, GLuint id, const std::string& name) |
| { |
| GLint location = gl.getAttribLocation(id, name.c_str()); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetAttribLocation"); |
| |
| return location; |
| } |
| |
| /** Query resource |
| * |
| * @param gl GL functions |
| * @param id Id of program |
| * @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(const Functions& gl, GLuint id, GLenum interface, GLuint index, GLenum property, |
| GLsizei buf_size, GLint* params) |
| { |
| gl.getProgramResourceiv(id, interface, index, 1 /* propCount */, &property, buf_size /* bufSize */, 0 /* length */, |
| params); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramResourceiv"); |
| } |
| |
| /** Get index of resource |
| * |
| * @param gl GL functions |
| * @param id Id of program |
| * @param name Name of resource |
| * @param interface Program interface to queried |
| * |
| * @return Location of attribute |
| **/ |
| GLuint Program::GetResourceIndex(const Functions& gl, GLuint id, const std::string& name, GLenum interface) |
| { |
| GLuint index = gl.getProgramResourceIndex(id, interface, name.c_str()); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramResourceIndex"); |
| |
| return index; |
| } |
| |
| /** Get location of attribute |
| * |
| * @param gl GL functions |
| * @param id Id of program |
| * @param name Name of attribute |
| * |
| * @return Location of uniform |
| **/ |
| GLint Program::GetUniformLocation(const Functions& gl, GLuint id, const std::string& name) |
| { |
| GLint location = gl.getUniformLocation(id, name.c_str()); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetUniformLocation"); |
| |
| return location; |
| } |
| |
| /** Constructor |
| * |
| * @param error_message Error message |
| * @param compute_shader Source code for compute stage |
| * @param fragment_shader Source code for fragment stage |
| * @param geometry_shader Source code for geometry stage |
| * @param tess_ctrl_shader Source code for tessellation control stage |
| * @param tess_eval_shader Source code for tessellation evaluation stage |
| * @param vertex_shader Source code for vertex stage |
| **/ |
| Program::BuildException::BuildException(const glw::GLchar* error_message, const std::string compute_shader, |
| const std::string fragment_shader, const std::string geometry_shader, |
| const std::string tess_ctrl_shader, const std::string tess_eval_shader, |
| const std::string vertex_shader) |
| : m_error_message(error_message) |
| , m_compute_shader(compute_shader) |
| , m_fragment_shader(fragment_shader) |
| , m_geometry_shader(geometry_shader) |
| , m_tess_ctrl_shader(tess_ctrl_shader) |
| , m_tess_eval_shader(tess_eval_shader) |
| , m_vertex_shader(vertex_shader) |
| { |
| } |
| |
| /** Overwrites std::exception::what method |
| * |
| * @return Message compossed from error message and shader sources |
| **/ |
| const char* Program::BuildException::what() const throw() |
| { |
| return "Failed to link program"; |
| } |
| |
| /** Logs error message and shader sources **/ |
| void Program::BuildException::log(deqp::Context& context) const |
| { |
| context.getTestContext().getLog() << tcu::TestLog::Message << "Link failure: " << m_error_message |
| << tcu::TestLog::EndMessage; |
| |
| Shader::LogSource(context, m_vertex_shader, Shader::VERTEX); |
| Shader::LogSource(context, m_tess_ctrl_shader, Shader::TESS_CTRL); |
| Shader::LogSource(context, m_tess_eval_shader, Shader::TESS_EVAL); |
| Shader::LogSource(context, m_geometry_shader, Shader::GEOMETRY); |
| Shader::LogSource(context, m_fragment_shader, Shader::FRAGMENT); |
| Shader::LogSource(context, m_compute_shader, Shader::COMPUTE); |
| } |
| |
| /** Constructor |
| * |
| * @param message Linking error message |
| **/ |
| Program::LinkageException::LinkageException(const glw::GLchar* message) : m_error_message(message) |
| { |
| /* Nothing to be done */ |
| } |
| |
| /** Returns error messages |
| * |
| * @return Linking error message |
| **/ |
| const char* Program::LinkageException::what() const throw() |
| { |
| return m_error_message.c_str(); |
| } |
| |
| /* Texture constants */ |
| const GLuint Texture::m_invalid_id = -1; |
| |
| /** Constructor. |
| * |
| * @param context CTS context. |
| **/ |
| Texture::Texture(deqp::Context& context) : m_id(m_invalid_id), m_context(context), m_type(TEX_2D) |
| { |
| /* Nothing to done here */ |
| } |
| |
| /** Destructor |
| * |
| **/ |
| Texture::~Texture() |
| { |
| Release(); |
| } |
| |
| /** Initialize texture instance |
| * |
| * @param tex_type Type of texture |
| * @param width Width of texture |
| * @param height Height of texture |
| * @param depth Depth of texture |
| * @param internal_format Internal format of texture |
| |