blob: 9203552eb050d7a48c054629c24396e6dbe64940 [file] [log] [blame]
/*-------------------------------------------------------------------------
* OpenGL Conformance Test Suite
* -----------------------------
*
* Copyright (c) 2014-2016 The Khronos Group Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/ /*!
* \file
* \brief
*/ /*-------------------------------------------------------------------*/
/**
* \file gl4cShadingLanguage420PackTests.cpp
* \brief Implements conformance tests for "Shading Language 420Pack" functionality.
*/ /*-------------------------------------------------------------------*/
#include "gl4cShadingLanguage420PackTests.hpp"
#include "gluContextInfo.hpp"
#include "gluDefs.hpp"
#include "glwEnums.hpp"
#include "glwFunctions.hpp"
#include "tcuTestLog.hpp"
#include <algorithm>
#include <iomanip>
#include <stdio.h>
#include <string.h>
#include <string>
#include <vector>
#define IS_DEBUG 0
using namespace glw;
namespace gl4cts
{
namespace GLSL420Pack
{
/** Check binding of uniform
*
* @param program Program object
* @param name Array name
* @param expected_binding Expected binding value
*
* @return true if binding is as expected, false otherwise
**/
bool Utils::checkUniformBinding(Utils::program& program, const glw::GLchar* name, glw::GLint expected_binding)
{
const GLint uniform_location = program.getUniformLocation(name);
if (-1 == uniform_location)
{
TCU_FAIL("Uniform is inactive");
}
GLint binding = program.getUniform1i(uniform_location);
return (expected_binding == binding);
}
/** Check binding of uniform array element at <index>
*
* @param program Program object
* @param name Array name
* @param index Index
* @param expected_binding Expected binding value
*
* @return true if binding is as expected, false otherwise
**/
bool Utils::checkUniformArrayBinding(Utils::program& program, const glw::GLchar* name, glw::GLuint index,
glw::GLint expected_binding)
{
GLchar buffer[64];
sprintf(buffer, "%s[%d]", name, index);
const GLint uniform_location = program.getUniformLocation(buffer);
if (-1 == uniform_location)
{
TCU_FAIL("Uniform is inactive");
}
GLint binding = program.getUniform1i(uniform_location);
return (expected_binding == binding);
}
/** Check if given qualifier is present in set
*
* @param qualifier Specific qualifier
* @param qualifiers Qualifiers' set
*
* @return true if qualifier is present, false otherwise
**/
bool Utils::doesContainQualifier(Utils::QUALIFIERS qualifier, const Utils::qualifierSet& qualifiers)
{
for (GLuint i = 0; i < qualifiers.size(); ++i)
{
if (qualifiers[i] == qualifier)
{
return true;
}
}
return false;
}
/** Check if given stage supports specific qualifier
*
* @param stage Shader stage
* @param storage Storage of variable
* @param qualifier Qualifier
*
* @return true if qualifier can be used in given stage, false otherwise
**/
bool Utils::doesStageSupportQualifier(Utils::SHADER_STAGES stage, Utils::VARIABLE_STORAGE storage,
Utils::QUALIFIERS qualifier)
{
bool result = true;
switch (stage)
{
case COMPUTE_SHADER:
switch (qualifier)
{
case QUAL_NONE:
case QUAL_UNIFORM:
case QUAL_LOWP:
case QUAL_MEDIUMP:
case QUAL_HIGHP:
case QUAL_INVARIANT:
result = true;
break;
default:
result = false;
break;
}
break;
case FRAGMENT_SHADER:
if (QUAL_PATCH == qualifier)
{
result = false;
}
else if ((OUTPUT == storage) &&
((QUAL_SMOOTH == qualifier) || (QUAL_NOPERSPECTIVE == qualifier) || (QUAL_FLAT == qualifier)))
{
result = false;
}
break;
case VERTEX_SHADER:
if (QUAL_PATCH == qualifier)
{
result = false;
}
else if ((INPUT == storage) &&
((QUAL_SMOOTH == qualifier) || (QUAL_NOPERSPECTIVE == qualifier) || (QUAL_FLAT == qualifier) ||
(QUAL_INVARIANT == qualifier) || (QUAL_CENTROID == qualifier) || (QUAL_SAMPLE == qualifier)))
{
result = false;
}
break;
case GEOMETRY_SHADER:
if (QUAL_PATCH == qualifier)
{
result = false;
}
break;
case TESS_CTRL_SHADER:
if ((INPUT == storage) && (QUAL_PATCH == qualifier))
{
result = false;
}
break;
case TESS_EVAL_SHADER:
if ((OUTPUT == storage) && (QUAL_PATCH == qualifier))
{
result = false;
}
break;
default:
break;
}
return result;
}
/** Get string for qualifier
*
* @param qualifier Qualifier
*
* @return A string for given qualifier
**/
const GLchar* Utils::getQualifierString(Utils::QUALIFIERS qualifier)
{
const GLchar* result = 0;
switch (qualifier)
{
case QUAL_NONE:
result = "";
break;
case QUAL_CONST:
result = "const";
break;
case QUAL_IN:
result = "in";
break;
case QUAL_OUT:
result = "out";
break;
case QUAL_INOUT:
result = "inout";
break;
case QUAL_UNIFORM:
result = "uniform";
break;
case QUAL_PATCH:
result = "patch";
break;
case QUAL_CENTROID:
result = "centroid";
break;
case QUAL_SAMPLE:
result = "sample";
break;
case QUAL_FLAT:
result = "flat";
break;
case QUAL_NOPERSPECTIVE:
result = "noperspective";
break;
case QUAL_SMOOTH:
result = "smooth";
break;
case QUAL_LOCATION:
result = "layout (location = LOC_VALUE)";
break;
case QUAL_LOWP:
result = "lowp";
break;
case QUAL_MEDIUMP:
result = "mediump";
break;
case QUAL_HIGHP:
result = "highp";
break;
case QUAL_PRECISE:
result = "precise";
break;
case QUAL_INVARIANT:
result = "invariant";
break;
default:
TCU_FAIL("Invalid enum");
};
return result;
}
/** Returns a string with set of qualifiers.
*
* @param qualifiers Set of qualifiers
*
* @return String
**/
std::string Utils::getQualifiersListString(const qualifierSet& qualifiers)
{
static const GLchar* qualifier_list = "QUALIFIER QUALIFIER_LIST";
const GLuint qualifier_list_length = static_cast<GLuint>(strlen(qualifier_list));
/* Tokens */
static const GLchar* token_qualifier = "QUALIFIER";
static const GLchar* token_qual_list = "QUALIFIER_LIST";
/* Variables */
std::string list = token_qual_list;
size_t position = 0;
/* Replace tokens */
for (GLuint i = 0; i < qualifiers.size(); ++i)
{
Utils::replaceToken(token_qual_list, position, qualifier_list, list);
position -= qualifier_list_length;
const GLchar* qualifier_str = getQualifierString(qualifiers[i]);
Utils::replaceToken(token_qualifier, position, qualifier_str, list);
}
Utils::replaceToken(token_qual_list, position, "", list);
return list;
}
/** Prepare a set of qualifiers for given shader stage and variable storage.
* Filters out not supported qualifiers from in_qualifiers
*
* @param in_qualifiers Origiranl set of qualifiers
* @param stage Shader stage
* @param storage Variable storage
*
* @return Set of qualifiers
**/
Utils::qualifierSet Utils::prepareQualifiersSet(const qualifierSet& in_qualifiers, SHADER_STAGES stage,
VARIABLE_STORAGE storage)
{
qualifierSet result;
for (GLuint i = 0; i < in_qualifiers.size(); ++i)
{
Utils::QUALIFIERS qualifier = in_qualifiers[i];
if (false == doesStageSupportQualifier(stage, storage, qualifier))
{
continue;
};
/* Replace wrong storage qualifiers */
if ((Utils::INPUT == storage) && ((Utils::QUAL_UNIFORM == qualifier) || (Utils::QUAL_OUT == qualifier)))
{
qualifier = QUAL_IN;
}
else if ((Utils::OUTPUT == storage) && ((Utils::QUAL_IN == qualifier) || (Utils::QUAL_UNIFORM == qualifier)))
{
qualifier = QUAL_OUT;
}
else if ((Utils::UNIFORM == storage) && ((Utils::QUAL_IN == qualifier) || (Utils::QUAL_OUT == qualifier)))
{
qualifier = QUAL_UNIFORM;
}
result.push_back(qualifier);
}
return result;
}
/** Get image type for given texture type
*
* @param type Texture type
*
* @return String representing sampler type
**/
const GLchar* Utils::getImageType(Utils::TEXTURE_TYPES type)
{
const GLchar* result = 0;
switch (type)
{
case TEX_BUFFER:
result = "imageBuffer";
break;
case TEX_2D:
result = "image2D";
break;
case TEX_2D_RECT:
result = "image2DRect";
break;
case TEX_2D_ARRAY:
result = "image2DArray";
break;
case TEX_3D:
result = "image3D";
break;
case TEX_CUBE:
result = "imageCube";
break;
case TEX_1D:
result = "image1D";
break;
case TEX_1D_ARRAY:
result = "image1DArray";
break;
default:
TCU_FAIL("Invalid enum");
}
return result;
}
/** Get number of coordinates required to address texture of given type
*
* @param type Type of texture
*
* @return Number of coordinates
**/
GLuint Utils::getNumberOfCoordinates(Utils::TEXTURE_TYPES type)
{
GLuint result = 0;
switch (type)
{
case TEX_BUFFER:
result = 1;
break;
case TEX_2D:
result = 2;
break;
case TEX_2D_RECT:
result = 2;
break;
case TEX_2D_ARRAY:
result = 3;
break;
case TEX_3D:
result = 3;
break;
case TEX_CUBE:
result = 3;
break;
case TEX_1D:
result = 1;
break;
case TEX_1D_ARRAY:
result = 2;
break;
default:
TCU_FAIL("Invalid enum");
}
return result;
}
/** Get sampler type for given texture type
*
* @param type Texture type
*
* @return String representing sampler type
**/
const GLchar* Utils::getSamplerType(Utils::TEXTURE_TYPES type)
{
const GLchar* result = 0;
switch (type)
{
case TEX_BUFFER:
result = "samplerBuffer";
break;
case TEX_2D:
result = "sampler2D";
break;
case TEX_2D_RECT:
result = "sampler2DRect";
break;
case TEX_2D_ARRAY:
result = "sampler2DArray";
break;
case TEX_3D:
result = "sampler3D";
break;
case TEX_CUBE:
result = "samplerCube";
break;
case TEX_1D:
result = "sampler1D";
break;
case TEX_1D_ARRAY:
result = "sampler1DArray";
break;
default:
TCU_FAIL("Invalid enum");
}
return result;
}
/** Get target for given texture type
*
* @param type Type of texture
*
* @return Target
**/
GLenum Utils::getTextureTartet(Utils::TEXTURE_TYPES type)
{
GLenum result = 0;
switch (type)
{
case TEX_BUFFER:
result = GL_TEXTURE_BUFFER;
break;
case TEX_2D:
result = GL_TEXTURE_2D;
break;
case TEX_2D_RECT:
result = GL_TEXTURE_RECTANGLE;
break;
case TEX_2D_ARRAY:
result = GL_TEXTURE_2D_ARRAY;
break;
case TEX_3D:
result = GL_TEXTURE_3D;
break;
case TEX_CUBE:
result = GL_TEXTURE_CUBE_MAP;
break;
case TEX_1D:
result = GL_TEXTURE_1D;
break;
case TEX_1D_ARRAY:
result = GL_TEXTURE_1D_ARRAY;
break;
default:
TCU_FAIL("Invalid enum");
}
return result;
}
/** Get name of given texture type
*
* @param type Texture type
*
* @return String representing name of texture type
**/
const GLchar* Utils::getTextureTypeName(Utils::TEXTURE_TYPES type)
{
const GLchar* result = 0;
switch (type)
{
case TEX_BUFFER:
result = "buffer";
break;
case TEX_2D:
result = "2D";
break;
case TEX_2D_RECT:
result = "2D rectangle";
break;
case TEX_2D_ARRAY:
result = "2D array";
break;
case TEX_3D:
result = "3D";
break;
case TEX_CUBE:
result = "cube";
break;
case TEX_1D:
result = "1D";
break;
case TEX_1D_ARRAY:
result = "1D array";
break;
default:
TCU_FAIL("Invalid enum");
}
return result;
}
/** Check if glsl support matrices for specific basic type
*
* @param type Basic type
*
* @return true if matrices of <type> are supported, false otherwise
**/
bool Utils::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;
}
/** Get string representing name of shader stage
*
* @param stage Shader stage
*
* @return String with name of shader stage
**/
const glw::GLchar* Utils::getShaderStageName(Utils::SHADER_STAGES stage)
{
const GLchar* result = 0;
switch (stage)
{
case COMPUTE_SHADER:
result = "compute";
break;
case VERTEX_SHADER:
result = "vertex";
break;
case TESS_CTRL_SHADER:
result = "tesselation control";
break;
case TESS_EVAL_SHADER:
result = "tesselation evaluation";
break;
case GEOMETRY_SHADER:
result = "geomtery";
break;
case FRAGMENT_SHADER:
result = "fragment";
break;
default:
TCU_FAIL("Invalid enum");
}
return result;
}
/** Get glsl name of specified type
*
* @param type Basic type
* @param n_columns Number of columns
* @param n_rows Number of rows
*
* @return Name of glsl type
**/
const glw::GLchar* Utils::getTypeName(TYPES type, glw::GLuint n_columns, glw::GLuint n_rows)
{
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 > n_columns) || (1 > n_rows) || (4 < n_columns) || (4 < n_rows))
{
return 0;
}
switch (type)
{
case FLOAT:
result = float_lut[n_columns - 1][n_rows - 1];
break;
case DOUBLE:
result = double_lut[n_columns - 1][n_rows - 1];
break;
case INT:
result = int_lut[n_rows - 1];
break;
case UINT:
result = uint_lut[n_rows - 1];
break;
default:
TCU_FAIL("Invliad 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
**/
Utils::uniformNdv Utils::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
**/
Utils::uniformNfv Utils::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
**/
Utils::uniformNiv Utils::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
**/
Utils::uniformNuiv Utils::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
**/
Utils::uniformMatrixNdv Utils::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
**/
Utils::uniformMatrixNfv Utils::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;
}
/** Prepare definition of input or output block's variable
*
* @param qualifiers Set of qualifiers
* @param type_name Name of type
* @param variable_name Meaningful part of variable name, eg. tex_coord
*
* @return Definition of variable
**/
std::string Utils::getBlockVariableDefinition(const qualifierSet& qualifiers, const glw::GLchar* type_name,
const glw::GLchar* variable_name)
{
/* Templates */
static const GLchar* def_template = "QUALIFIER_LISTTYPE VARIABLE_NAME";
/* Tokens */
static const GLchar* token_type = "TYPE";
static const GLchar* token_variable_name = "VARIABLE_NAME";
static const GLchar* token_qual_list = "QUALIFIER_LIST";
/* Variables */
std::string variable_definition = def_template;
size_t position = 0;
/* Get qualifiers list */
const std::string& list = getQualifiersListString(qualifiers);
/* Replace tokens */
Utils::replaceToken(token_qual_list, position, list.c_str(), variable_definition);
Utils::replaceToken(token_type, position, type_name, variable_definition);
Utils::replaceToken(token_variable_name, position, variable_name, variable_definition);
/* Done */
return variable_definition;
}
/** Prepare reference to input or output variable
*
* @param flavour "Flavour" of variable
* @param variable_name Meaningful part of variable name, eg. tex_coord
* @param block_name Name of block
*
* @return Reference to variable
**/
std::string Utils::getBlockVariableReference(VARIABLE_FLAVOUR flavour, const glw::GLchar* variable_name,
const glw::GLchar* block_name)
{
/* Templates */
static const GLchar* ref_template = "BLOCK_NAME.VARIABLE_NAME";
static const GLchar* array_ref_template = "BLOCK_NAME[0].VARIABLE_NAME";
static const GLchar* tcs_ref_template = "BLOCK_NAME[gl_InvocationID].VARIABLE_NAME";
/* Token */
static const GLchar* token_block_name = "BLOCK_NAME";
static const GLchar* token_variable_name = "VARIABLE_NAME";
/* Variables */
std::string variable_definition;
size_t position = 0;
/* Select variable reference template */
switch (flavour)
{
case BASIC:
variable_definition = ref_template;
break;
case ARRAY:
variable_definition = array_ref_template;
break;
case INDEXED_BY_INVOCATION_ID:
variable_definition = tcs_ref_template;
break;
default:
variable_definition = ref_template;
break;
}
/* Replace tokens */
replaceAllTokens(token_block_name, block_name, variable_definition);
replaceToken(token_variable_name, position, variable_name, variable_definition);
/* Done */
return variable_definition;
}
/** Prepare definition of input or output variable
*
* @param flavour "Flavour" of variable
* @param qualifiers Set of qualifiers
* @param type_name Name of type
* @param variable_name Meaningful part of variable name, eg. tex_coord
*
* @return Definition of variable
**/
std::string Utils::getVariableDefinition(VARIABLE_FLAVOUR flavour, const qualifierSet& qualifiers,
const glw::GLchar* type_name, const glw::GLchar* variable_name)
{
/* Templates */
static const GLchar* def_template = "QUALIFIER_LISTTYPE VARIABLE_NAME";
static const GLchar* def_array_template = "QUALIFIER_LISTTYPE VARIABLE_NAME[]";
/* Tokens */
static const GLchar* token_type = "TYPE";
static const GLchar* token_variable_name = "VARIABLE_NAME";
static const GLchar* token_qual_list = "QUALIFIER_LIST";
/* Variables */
std::string variable_definition;
size_t position = 0;
/* Select variable definition template */
switch (flavour)
{
case BASIC:
variable_definition = def_template;
break;
case ARRAY:
case INDEXED_BY_INVOCATION_ID:
variable_definition = def_array_template;
break;
default:
TCU_FAIL("Invliad enum");
break;
}
/* Get qualifiers list */
const std::string& list = getQualifiersListString(qualifiers);
/* Replace tokens */
replaceToken(token_qual_list, position, list.c_str(), variable_definition);
replaceToken(token_type, position, type_name, variable_definition);
replaceToken(token_variable_name, position, variable_name, variable_definition);
/* Done */
return variable_definition;
}
/** Get "flavour" of variable
*
* @param stage Shader stage
* @param storage Storage of variable
* @param qualifiers Set of qualifiers for variable
*
* @return "Flavour" of variable
**/
Utils::VARIABLE_FLAVOUR Utils::getVariableFlavour(SHADER_STAGES stage, VARIABLE_STORAGE storage,
const qualifierSet& qualifiers)
{
VARIABLE_FLAVOUR result;
if (UNIFORM == storage)
{
result = BASIC;
}
else
{
switch (stage)
{
case Utils::GEOMETRY_SHADER:
if (Utils::INPUT == storage)
{
result = ARRAY;
}
else /* OUTPUT */
{
result = BASIC;
}
break;
case Utils::TESS_EVAL_SHADER:
if ((false == Utils::doesContainQualifier(Utils::QUAL_PATCH, qualifiers)) && (Utils::INPUT == storage))
{
result = ARRAY;
}
else /* OUTPUT */
{
result = BASIC;
}
break;
case Utils::TESS_CTRL_SHADER:
if ((true == Utils::doesContainQualifier(Utils::QUAL_PATCH, qualifiers)) && (Utils::OUTPUT == storage))
{
result = BASIC;
}
else
{
result = INDEXED_BY_INVOCATION_ID;
}
break;
case Utils::VERTEX_SHADER:
case Utils::FRAGMENT_SHADER:
result = BASIC;
break;
default:
TCU_FAIL("Invliad enum");
break;
}
}
return result;
}
/** Prepare name of input or output variable
*
* @param stage Shader stage
* @param storage Storage of variable
* @param variable_name Meaningful part of variable name, eg. tex_coord
*
* @return Name of variable
**/
std::string Utils::getVariableName(SHADER_STAGES stage, VARIABLE_STORAGE storage, const glw::GLchar* variable_name)
{
/* Variable name template */
static const GLchar* variable_name_template = "PRECEEDING_PREFIX_VARIABLE_NAME";
/* Tokens */
static const GLchar* token_preceeding = "PRECEEDING";
static const GLchar* token_prefix = "PREFIX";
static const GLchar* token_variable_name = "VARIABLE_NAME";
static const GLchar* prefixes[Utils::STORAGE_MAX][Utils::SHADER_STAGES_MAX][2] = {
/* COMPUTE, VERTEX, TCS, TES, GEOMETRY, FRAGMENT */
{ { "", "" },
{ "in", "vs" },
{ "vs", "tcs" },
{ "tcs", "tes" },
{ "tes", "gs" },
{ "gs", "fs" } }, /* INPUT */
{ { "", "" },
{ "vs", "tcs" },
{ "tcs", "tes" },
{ "tes", "gs" },
{ "gs", "fs" },
{ "fs", "out" } }, /* OUTPUT */
{ { "uni", "comp" },
{ "uni", "vs" },
{ "uni", "tcs" },
{ "uni", "tes" },
{ "uni", "gs" },
{ "uni", "fs" } } /* UNIFORM */
};
/* Variables */
const GLchar* preceeding = prefixes[storage][stage][0];
const GLchar* prefix = prefixes[storage][stage][1];
std::string name = variable_name_template;
size_t position = 0;
/* Replace tokens */
Utils::replaceToken(token_preceeding, position, preceeding, name);
Utils::replaceToken(token_prefix, position, prefix, name);
Utils::replaceToken(token_variable_name, position, variable_name, name);
/* Done */
return name;
}
/** Prepare reference to input or output variable
*
* @param flavour "Flavour" of variable
* @param variable_name Meaningful part of variable name, eg. tex_coord
*
* @return Reference to variable
**/
std::string Utils::getVariableReference(VARIABLE_FLAVOUR flavour, const glw::GLchar* variable_name)
{
/* Templates */
static const GLchar* ref_template = "VARIABLE_NAME";
static const GLchar* array_ref_template = "VARIABLE_NAME[0]";
static const GLchar* tcs_ref_template = "VARIABLE_NAME[gl_InvocationID]";
/* Token */
static const GLchar* token_variable_name = "VARIABLE_NAME";
/* Variables */
std::string variable_definition;
size_t position = 0;
/* Select variable reference template */
switch (flavour)
{
case BASIC:
variable_definition = ref_template;
break;
case ARRAY:
variable_definition = array_ref_template;
break;
case INDEXED_BY_INVOCATION_ID:
variable_definition = tcs_ref_template;
break;
default:
variable_definition = ref_template;
break;
}
/* Replace token */
Utils::replaceToken(token_variable_name, position, variable_name, variable_definition);
/* Done */
return variable_definition;
}
/** Prepare definition and reference string for block varaible
*
* @param in_stage Shader stage
* @param in_storage Storage of variable
* @param in_qualifiers Set of qualifiers
* @param in_type_name Type name
* @param in_variable_name Meaningful part of variable name, like "color"
* @param in_block_name Name of block, like "input"
* @param out_definition Definition string
* @param out_reference Reference string
**/
void Utils::prepareBlockVariableStrings(Utils::SHADER_STAGES in_stage, Utils::VARIABLE_STORAGE in_storage,
const Utils::qualifierSet& in_qualifiers, const glw::GLchar* in_type_name,
const glw::GLchar* in_variable_name, const glw::GLchar* in_block_name,
std::string& out_definition, std::string& out_reference)
{
VARIABLE_FLAVOUR flavour = getVariableFlavour(in_stage, in_storage, in_qualifiers);
const qualifierSet& qualifiers = prepareQualifiersSet(in_qualifiers, in_stage, in_storage);
const std::string& name = getVariableName(in_stage, in_storage, in_variable_name);
out_definition = getBlockVariableDefinition(qualifiers, in_type_name, name.c_str());
out_reference = getBlockVariableReference(flavour, name.c_str(), in_block_name);
}
/** Prepare definition and reference string for block varaible
*
* @param in_stage Shader stage
* @param in_storage Storage of variable
* @param in_qualifiers Set of qualifiers
* @param in_type_name Type name
* @param in_variable_name Meaningful part of variable name, like "color"
* @param out_definition Definition string
* @param out_reference Reference string
**/
void Utils::prepareVariableStrings(Utils::SHADER_STAGES in_stage, Utils::VARIABLE_STORAGE in_storage,
const Utils::qualifierSet& in_qualifiers, const glw::GLchar* in_type_name,
const glw::GLchar* in_variable_name, std::string& out_definition,
std::string& out_reference)
{
VARIABLE_FLAVOUR flavour = getVariableFlavour(in_stage, in_storage, in_qualifiers);
const qualifierSet& qualifiers = prepareQualifiersSet(in_qualifiers, in_stage, in_storage);
const std::string& name = getVariableName(in_stage, in_storage, in_variable_name);
out_definition = getVariableDefinition(flavour, qualifiers, in_type_name, name.c_str());
out_reference = getVariableReference(flavour, name.c_str());
}
/** Returns string with UTF8 character for current test case
*
* @return String with UTF8 character
**/
const GLchar* Utils::getUtf8Character(Utils::UTF8_CHARACTERS character)
{
static const unsigned char two_bytes[] = { 0xd7, 0x84, 0x00 };
static const unsigned char three_bytes[] = { 0xe3, 0x82, 0x81, 0x00 };
static const unsigned char four_bytes[] = { 0xf0, 0x93, 0x83, 0x93, 0x00 };
static const unsigned char five_bytes[] = { 0xfa, 0x82, 0x82, 0x82, 0x82, 0x00 };
static const unsigned char six_bytes[] = { 0xfd, 0x82, 0x82, 0x82, 0x82, 0x82, 0x00 };
static const unsigned char redundant_bytes[] = { 0xf2, 0x80, 0x80, 0x5e, 0x00 };
const GLchar* result = 0;
switch (character)
{
case TWO_BYTES:
result = (const GLchar*)two_bytes;
break;
case THREE_BYTES:
result = (const GLchar*)three_bytes;
break;
case FOUR_BYTES:
result = (const GLchar*)four_bytes;
break;
case FIVE_BYTES:
result = (const GLchar*)five_bytes;
break;
case SIX_BYTES:
result = (const GLchar*)six_bytes;
break;
case REDUNDANT_ASCII:
result = (const GLchar*)redundant_bytes;
break;
case EMPTY:
result = "";
break;
default:
TCU_FAIL("Invalid enum");
}
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 Utils::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 Utils::isGLVersionAtLeast(const glw::Functions& gl, glw::GLint required_major, glw::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 Utils::replaceToken(const glw::GLchar* token, size_t& search_position, const glw::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);
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 Utils::replaceAllTokens(const glw::GLchar* token, const glw::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);
}
}
/** Constructor
*
* @param context Test context
* @param test_name Test name
* @param test_description Test description
**/
TestBase::TestBase(deqp::Context& context, const glw::GLchar* test_name, const glw::GLchar* test_description)
: TestCase(context, test_name, test_description)
, m_is_compute_shader_supported(false)
, m_is_explicit_uniform_location(false)
, m_is_shader_language_420pack(false)
{
/* Nothing to be done here */
}
/** Execute test
*
* @return tcu::TestNode::CONTINUE after executing test case, tcu::TestNode::STOP otherwise
**/
tcu::TestNode::IterateResult TestBase::iterate()
{
/* GL entry points */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Check extension support and version */
m_is_explicit_uniform_location = Utils::isExtensionSupported(m_context, "GL_ARB_explicit_uniform_location");
m_is_shader_language_420pack = Utils::isExtensionSupported(m_context, "GL_ARB_shading_language_420pack");
m_is_compute_shader_supported = Utils::isGLVersionAtLeast(gl, 4, 3);
/* Execute test */
bool test_result = test();
/* Set result */
if (true == test_result)
{
m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
/* Done */
return tcu::TestNode::STOP;
}
/** Basic implementation of getShaderSourceConfig method.
*
* @param out_n_parts Number of source parts used by this test case
* @param out_use_lengths If source lengths shall be provided to compiler
**/
void TestBase::getShaderSourceConfig(glw::GLuint& out_n_parts, bool& out_use_lengths)
{
out_n_parts = 1;
out_use_lengths = false;
}
/** Basic implementation of prepareNextTestCase method.
*
* @param test_case_index Index of test case
*
* @return true if index is -1 or 0, false otherwise
**/
bool TestBase::prepareNextTestCase(GLuint test_case_index)
{
if (((GLuint)-1 == test_case_index) || (0 == test_case_index))
{
return true;
}
else
{
return false;
}
}
/** Basic implementation of prepareUniforms method
*
* @param ignored
**/
void TestBase::prepareUniforms(Utils::program& /* program */)
{
/* Nothing to be done */
}
/** Basic implementation of testInit method
*
* @return true if test can be executed, false otherwise
**/
bool TestBase::testInit()
{
return true;
}
/** Get layout specific for given stage
*
* @param stage Shader stage
*
* @return Stage specific part
**/
const GLchar* TestBase::getStageSpecificLayout(Utils::SHADER_STAGES stage) const
{
static const GLchar* stage_layout_geometry = "layout(points) in;\n"
"layout(triangle_strip, max_vertices = 4) out;\n";
static const GLchar* stage_layout_tess_ctrl = "layout(vertices = 1) out;\n";
static const GLchar* stage_layout_tess_eval = "layout(isolines, point_mode) in;\n";
const GLchar* result = "";
switch (stage)
{
case Utils::GEOMETRY_SHADER:
result = stage_layout_geometry;
break;
case Utils::TESS_CTRL_SHADER:
result = stage_layout_tess_ctrl;
break;
case Utils::TESS_EVAL_SHADER:
result = stage_layout_tess_eval;
break;
case Utils::VERTEX_SHADER:
case Utils::FRAGMENT_SHADER:
default:
break;
}
return result;
}
/** Get "version" string
*
* @param stage Shader stage, compute shader will use 430
* @param use_version_400 Select if 400 or 420 should be used
*
* @return Version string
**/
const GLchar* TestBase::getVersionString(Utils::SHADER_STAGES stage, bool use_version_400) const
{
static const GLchar* version_400 = "#version 400\n"
"#extension GL_ARB_shading_language_420pack : require\n"
"#extension GL_ARB_separate_shader_objects : enable";
static const GLchar* version_420 = "#version 420";
static const GLchar* version_430 = "#version 430";
const GLchar* result = "";
if (Utils::COMPUTE_SHADER == stage)
{
result = version_430;
}
else if (true == use_version_400)
{
result = version_400;
}
else
{
result = version_420;
}
return result;
}
/** Initialize shaderSource instance, reserve storage and prepare shader source
*
* @param in_stage Shader stage
* @param in_use_version_400 If version 400 or 420 should be used
* @param out_source Shader source instance
**/
void TestBase::initShaderSource(Utils::SHADER_STAGES in_stage, bool in_use_version_400, Utils::shaderSource& out_source)
{
/* Shader source configuration */
glw::GLuint n_parts = 0;
bool use_lengths = false;
getShaderSourceConfig(n_parts, use_lengths);
out_source.m_parts.resize(n_parts);
out_source.m_use_lengths = use_lengths;
/* Request child class to prepare shader sources */
prepareShaderSource(in_stage, in_use_version_400, out_source);
/* Prepare source lengths */
if (true == use_lengths)
{
for (GLuint i = 0; i < n_parts; ++i)
{
out_source.m_parts[i].m_length = static_cast<glw::GLint>(out_source.m_parts[i].m_code.length());
out_source.m_parts[i].m_code.append("This should be ignored by compiler, as source length is provided");
}
}
else
{
for (GLuint i = 0; i < n_parts; ++i)
{
out_source.m_parts[i].m_length = 0;
}
}
}
/** Execute test
*
* @return true if test pass, false otherwise
**/
bool TestBase::test()
{
bool result = true;
GLuint test_case_index = 0;
/* Prepare test cases */
testInit();
/* GL entry points */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Tesselation patch set up */
gl.patchParameteri(GL_PATCH_VERTICES, 1);
GLU_EXPECT_NO_ERROR(gl.getError(), "PatchParameteri");
while (true == prepareNextTestCase(test_case_index))
{
bool case_result = true;
/* Execute drawing case */
if (false == testDrawArray(false))
{
case_result = false;
}
if (true == m_is_shader_language_420pack)
{
if (false == testDrawArray(true))
{
case_result = false;
}
}
/* Execute compute shader case */
if (true == m_is_compute_shader_supported)
{
if (false == testCompute())
{
case_result = false;
}
}
/* Log failure */
if (false == case_result)
{
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Test case failed."
<< tcu::TestLog::EndMessage;
result = false;
}
/* Go to next test case */
test_case_index += 1;
}
/* Done */
return result;
}
int TestBase::maxImageUniforms(Utils::SHADER_STAGES stage) const
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
GLint max_image_uniforms;
switch (stage)
{
case Utils::COMPUTE_SHADER:
gl.getIntegerv(GL_MAX_COMPUTE_IMAGE_UNIFORMS, &max_image_uniforms);
break;
case Utils::FRAGMENT_SHADER:
gl.getIntegerv(GL_MAX_FRAGMENT_IMAGE_UNIFORMS, &max_image_uniforms);
break;
case Utils::GEOMETRY_SHADER:
gl.getIntegerv(GL_MAX_GEOMETRY_IMAGE_UNIFORMS, &max_image_uniforms);
break;
case Utils::TESS_CTRL_SHADER:
gl.getIntegerv(GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS, &max_image_uniforms);
break;
case Utils::TESS_EVAL_SHADER:
gl.getIntegerv(GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS, &max_image_uniforms);
break;
case Utils::VERTEX_SHADER:
gl.getIntegerv(GL_MAX_VERTEX_IMAGE_UNIFORMS, &max_image_uniforms);
break;
default:
TCU_FAIL("Invalid enum");
break;
}
return max_image_uniforms;
}
/** Constructor
*
* @param context Test context
* @param test_name Name of test
* @param test_description Description of test
**/
APITestBase::APITestBase(deqp::Context& context, const glw::GLchar* test_name, const glw::GLchar* test_description)
: TestBase(context, test_name, test_description)
{
/* Nothing to be done here */
}
/** Execute test with compute shader
*
* @return true if test pass, false otherwise
**/
bool APITestBase::testCompute()
{
/* GL objects */
Utils::program program(m_context);
/* Shaders */
Utils::shaderSource compute_shader;
initShaderSource(Utils::COMPUTE_SHADER, false, compute_shader);
/* Check if test support compute shaders */
if (true == compute_shader.m_parts[0].m_code.empty())
{
return true;
}
/* Build program */
try
{
program.build(compute_shader, 0 /* fragment shader */, 0 /* geometry shader */,
0 /* tesselation control shader */, 0 /* tesselation evaluation shader */, 0 /* vertex shader */,
0 /* varying names */, 0 /* n varying names */, false);
}
catch (Utils::shaderCompilationException& exc)
{
/* Something wrong with compilation, test case failed */
tcu::MessageBuilder message = m_context.getTestContext().getLog() << tcu::TestLog::Message;
message << "Shader compilation failed. Error message: " << exc.m_error_message;
Utils::program::printShaderSource(exc.m_shader_source, message);
message << tcu::TestLog::EndMessage;
return false;
}
catch (Utils::programLinkageException& exc)
{
/* Something wrong with linking, test case failed */
m_context.getTestContext().getLog() << tcu::TestLog::Message
<< "Program linking failed. Error message: " << exc.m_error_message
<< tcu::TestLog::EndMessage;
return false;
}
/* Set current program */
program.use();
/* Return result of verification */
return checkResults(program);
}
/** Execute test with VS, TCS, TES, GS and FS
*
* @param use_version_400 Select if 400 or 420 should be used
*
* @return true if test pass, false otherwise
**/
bool APITestBase::testDrawArray(bool use_version_400)
{
/* GL objects */
Utils::program program(m_context);
/* Shaders */
Utils::shaderSource fragment_data;
Utils::shaderSource geometry_data;
Utils::shaderSource tess_ctrl_data;
Utils::shaderSource tess_eval_data;
Utils::shaderSource vertex_data;
initShaderSource(Utils::FRAGMENT_SHADER, use_version_400, fragment_data);
initShaderSource(Utils::GEOMETRY_SHADER, use_version_400, geometry_data);
initShaderSource(Utils::TESS_CTRL_SHADER, use_version_400, tess_ctrl_data);
initShaderSource(Utils::TESS_EVAL_SHADER, use_version_400, tess_eval_data);
initShaderSource(Utils::VERTEX_SHADER, use_version_400, vertex_data);
/* Build program */
try
{
program.build(0 /* compute shader */, fragment_data, geometry_data, tess_ctrl_data, tess_eval_data, vertex_data,
0 /* varying names */, 0 /* n varying names */, false);
}
catch (Utils::shaderCompilationException& exc)
{
/* Something wrong with compilation, test case failed */
tcu::MessageBuilder message = m_context.getTestContext().getLog() << tcu::TestLog::Message;
message << "Shader compilation failed. Error message: " << exc.m_error_message;
Utils::program::printShaderSource(exc.m_shader_source, message);
message << tcu::TestLog::EndMessage;
return false;
}
catch (Utils::programLinkageException& exc)
{
/* Something wrong with linking, test case failed */
m_context.getTestContext().getLog() << tcu::TestLog::Message
<< "Program linking failed. Error message: " << exc.m_error_message
<< tcu::TestLog::EndMessage;
return false;
}
/* Set current program */
program.use();
/* Return result of verification */
return checkResults(program);
}
/* Constants used by GLSLTestBase */
const glw::GLenum GLSLTestBase::m_color_texture_internal_format = GL_RGBA8;
const glw::GLenum GLSLTestBase::m_color_texture_format = GL_RGBA;
const glw::GLenum GLSLTestBase::m_color_texture_type = GL_UNSIGNED_BYTE;
const glw::GLuint GLSLTestBase::m_color_texture_width = 16;
const glw::GLuint GLSLTestBase::m_color_texture_height = 16;
/** Constructor
*
* @param context Test context
* @param test_name Test name
* @param test_description Test description
**/
GLSLTestBase::GLSLTestBase(deqp::Context& context, const glw::GLchar* test_name, const glw::GLchar* test_description)
: TestBase(context, test_name, test_description)
{
/* Nothing to be done here */
}
/** Basic implementation of prepareSourceTexture method.
*
* @param ignored Texture instance
*
* @return 0
**/
const GLchar* GLSLTestBase::prepareSourceTexture(Utils::texture&)
{
return 0;
}
/** Basic implementation of prepareVertexBuffer method.
*
* @param ignored Program instance
* @param ignored Buffer instance
* @param vao VertexArray instance
*
* @return 0
**/
void GLSLTestBase::prepareVertexBuffer(const Utils::program&, Utils::buffer&, Utils::vertexArray& vao)
{
vao.generate();
vao.bind();
}
/** Basic implementation of verifyAdditionalResults
*
* @return true
**/
bool GLSLTestBase::verifyAdditionalResults() const
{
return true;
}
/** Basic implementation of releaseResource method
*
* @param ignored
**/
void GLSLTestBase::releaseResource()
{
/* Nothing to be done */
}
/** Bind texture to first image unit and set image uniform to that unit
*
* @param program Program object
* @param texture Texture object
* @param uniform_name Name of image uniform
**/
void GLSLTestBase::bindTextureToimage(Utils::program& program, Utils::texture& texture,
const glw::GLchar* uniform_name) const
{
/* GL entry points */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
gl.bindImageTexture(0 /* unit */, texture.m_id, 0 /* level */, GL_FALSE /* layered */, 0 /* layer */, GL_WRITE_ONLY,
GL_RGBA8);
GLU_EXPECT_NO_ERROR(gl.getError(), "BindImageTexture");
GLint location = program.getUniformLocation(uniform_name);
gl.uniform1i(location, 0);
GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform1i");
}
/** Bind texture to first texture unit and set sampler uniform to that unit
*
* @param program Program object
* @param texture Texture object
* @param uniform_name Name of sampler uniform
**/
void GLSLTestBase::bindTextureToSampler(Utils::program& program, Utils::texture& texture,
const glw::GLchar* uniform_name) const
{
/* GL entry points */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
gl.activeTexture(GL_TEXTURE0);
GLU_EXPECT_NO_ERROR(gl.getError(), "ActiveTexture");
texture.bind();
GLint location = program.getUniformLocation(uniform_name);
gl.uniform1i(location, 0);
GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform1i");
}
/** Check contents of texture. It is expected that it will be filled with green color
*
* @param color_texture Texture that will be verified
*
* @return true if texture is all green, false otherwise
**/
bool GLSLTestBase::checkResults(Utils::texture& color_texture) const
{
static const GLuint green_color = 0xff00ff00;
const GLuint texture_data_size = m_color_texture_width * m_color_texture_height;
std::vector<glw::GLuint> texture_data;
texture_data.resize(texture_data_size);
color_texture.get(m_color_texture_format, m_color_texture_type, &texture_data[0]);
for (GLuint i = 0; i < texture_data_size; ++i)
{
if (green_color != texture_data[i])
{
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Invalid texel: " << std::setbase(16)
<< std::setfill('0') << std::setw(8) << texture_data[i]
<< " at index: " << i << tcu::TestLog::EndMessage;
return false;
}
}
return verifyAdditionalResults();
}
/** Prepare framebuffer with texture used as attachment
*
* @param framebuffer Framebuffer
* @param color_texture Textue used as color attachment 0
**/
void GLSLTestBase::prepareFramebuffer(Utils::framebuffer& framebuffer, Utils::texture& color_texture) const
{
framebuffer.generate();
color_texture.create(m_color_texture_width, m_color_texture_height, m_color_texture_internal_format);
framebuffer.attachTexture(GL_COLOR_ATTACHMENT0, color_texture.m_id, m_color_texture_width, m_color_texture_height);
framebuffer.clearColor(0.0f, 0.0f, 0.0f, 0.0f);
framebuffer.clear(GL_COLOR_BUFFER_BIT);
}
/** Prepare texture and bind it to image uniform
*
* @param framebuffer Framebuffer
* @param color_texture Textue used as color attachment 0
**/
void GLSLTestBase::prepareImage(Utils::program& program, Utils::texture& color_texture) const
{
color_texture.create(m_color_texture_width, m_color_texture_height, m_color_texture_internal_format);
bindTextureToimage(program, color_texture, "uni_image");
}
/** Execute test with compute shader
*
* @return true if test pass, false otherwise
**/
bool GLSLTestBase::testCompute()
{
/* Test Result */
bool result = true;
/* GL objects */
Utils::texture color_tex(m_context);
Utils::program program(m_context);
Utils::texture source_tex(m_context);
/* Shaders */
Utils::shaderSource compute_shader;
initShaderSource(Utils::COMPUTE_SHADER, false, compute_shader);
/* Check if test support compute shaders */
if (true == compute_shader.m_parts[0].m_code.empty())
{
return true;
}
/* Build program */
try
{
program.build(compute_shader, 0 /* fragment shader */, 0 /* geometry shader */,
0 /* tesselation control shader */, 0 /* tesselation evaluation shader */, 0 /* vertex shader */,
0 /* varying names */, 0 /* n varying names */, false);
}
catch (Utils::shaderCompilationException& exc)
{
/* Something wrong with compilation, test case failed */
tcu::MessageBuilder message = m_context.getTestContext().getLog() << tcu::TestLog::Message;
message << "Shader compilation failed. Error message: " << exc.m_error_message;
Utils::program::printShaderSource(exc.m_shader_source, message);
message << tcu::TestLog::EndMessage;
return false;
}
catch (Utils::programLinkageException& exc)
{
/* Something wrong with linking, test case failed */
m_context.getTestContext().getLog() << tcu::TestLog::Message
<< "Program linking failed. Error message: " << exc.m_error_message
<< tcu::TestLog::EndMessage;
return false;
}
/* Log shaders, for debugging */
#if IS_DEBUG
{
tcu::MessageBuilder message = m_context.getTestContext().getLog() << tcu::TestLog::Message;
Utils::program::printShaderSource(compute_shader, message);
message << tcu::TestLog::EndMessage;
}
#endif /* IS_DEBUG */
/* Set current program */
program.use();
/* Prepare image unit */
prepareImage(program, color_tex);
/* Test specific preparation of source texture */
const GLchar* sampler_name = prepareSourceTexture(source_tex);
if (0 != sampler_name)
{
bindTextureToSampler(program, source_tex, sampler_name);
}
/* Set up uniforms */
prepareUniforms(program);
/* GL entry points */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Draw */
gl.dispatchCompute(m_color_texture_width, m_color_texture_height, 1);
GLU_EXPECT_NO_ERROR(gl.getError(), "DispatchCompute");
/* Return result of verification */
result = checkResults(color_tex);
/* Release extra resource for the test */
releaseResource();
return result;
}
/** Execute test with draw array operation
*
* @param use_version_400 Select if 400 or 420 should be used
*
* @return true if test pass, false otherwise
**/
bool GLSLTestBase::testDrawArray(bool use_version_400)
{
/* Test Result */
bool result = true;
/* GL objects */
Utils::texture color_tex(m_context);
Utils::framebuffer framebuffer(m_context);
Utils::program program(m_context);
Utils::texture source_tex(m_context);
Utils::vertexArray vao(m_context);
Utils::buffer vertex_buffer(m_context);
/* Shaders */
Utils::shaderSource fragment_data;
Utils::shaderSource geometry_data;
Utils::shaderSource tess_ctrl_data;
Utils::shaderSource tess_eval_data;
Utils::shaderSource vertex_data;
initShaderSource(Utils::FRAGMENT_SHADER, use_version_400, fragment_data);
initShaderSource(Utils::GEOMETRY_SHADER, use_version_400, geometry_data);
initShaderSource(Utils::TESS_CTRL_SHADER, use_version_400, tess_ctrl_data);
initShaderSource(Utils::TESS_EVAL_SHADER, use_version_400, tess_eval_data);
initShaderSource(Utils::VERTEX_SHADER, use_version_400, vertex_data);
/* Build program */
try
{
program.build(0 /* compute shader */, fragment_data, geometry_data, tess_ctrl_data, tess_eval_data, vertex_data,
0 /* varying names */, 0 /* n varying names */, false);
}
catch (Utils::shaderCompilationException& exc)
{
/* Something wrong with compilation, test case failed */
tcu::MessageBuilder message = m_context.getTestContext().getLog() << tcu::TestLog::Message;
message << "Shader compilation failed. Error message: " << exc.m_error_message;
Utils::program::printShaderSource(exc.m_shader_source, message);
message << tcu::TestLog::EndMessage;
return false;
}
catch (Utils::programLinkageException& exc)
{
/* Something wrong with linking, test case failed */
m_context.getTestContext().getLog() << tcu::TestLog::Message
<< "Program linking failed. Error message: " << exc.m_error_message
<< tcu::TestLog::EndMessage;
return false;
}
/* Log shaders, for debugging */
#if IS_DEBUG
{
const Utils::shaderSource* data[] = { &vertex_data, &tess_ctrl_data, &tess_eval_data, &geometry_data,
&fragment_data };
tcu::MessageBuilder message = m_context.getTestContext().getLog() << tcu::TestLog::Message;
for (GLuint i = 0; i < 5; ++i)
{
Utils::program::printShaderSource(*data[i], message);
}
message << tcu::TestLog::EndMessage;
}
#endif /* IS_DEBUG */
/* Test specific preparation of vertex buffer and vao*/
prepareVertexBuffer(program, vertex_buffer, vao);
/* Set current program */
program.use();
/* Prepare framebuffer */
prepareFramebuffer(framebuffer, color_tex);
/* Test specific preparation of source texture */
const GLchar* sampler_name = prepareSourceTexture(source_tex);
if (0 != sampler_name)
{
bindTextureToSampler(program, source_tex, sampler_name);
}
/* Set up uniforms */
prepareUniforms(program);
/* GL entry points */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Draw */
gl.drawArrays(GL_PATCHES, 0 /* first */, 1 /* count */);
GLU_EXPECT_NO_ERROR(gl.getError(), "DrawArrays");
/* Return result of verification */
result = checkResults(color_tex);
/* Release extra resource for the test */
releaseResource();
return result;
}
/** Constructor
*
* @param context Test context
* @param test_name Test name
* @param test_description Test description
**/
NegativeTestBase::NegativeTestBase(deqp::Context& context, const glw::GLchar* test_name,
const glw::GLchar* test_description)
: TestBase(context, test_name, test_description)
{
/* Nothing to be done here */
}
/** Execute test with compute shader
*
* @return true if test pass, false otherwise
**/
bool NegativeTestBase::testCompute()
{
/* GL objects */
Utils::program program(m_context);
/* Shaders */
Utils::shaderSource conmpute_data;
initShaderSource(Utils::COMPUTE_SHADER, false, conmpute_data);
/* Build program */
try
{
program.build(conmpute_data /* compute shader */, 0 /* fragment shader */, 0 /* geometry shader */,
0 /* tesselation control shader */, 0 /* tesselation evaluation shader */, 0 /* vertex shader */,
0 /* varying names */, 0 /* n varying names */, false);
}
catch (Utils::shaderCompilationException& exc)
{
/* Compilation failed, as expected. Verify that reason of failure is as expected */
m_context.getTestContext().getLog() << tcu::TestLog::Message
<< "Shader compilation error message: " << exc.m_error_message
<< tcu::TestLog::EndMessage;
return true;
}
catch (Utils::programLinkageException& exc)
{
/* Something wrong with linking, test case failed */
m_context.getTestContext().getLog() << tcu::TestLog::Message
<< "Program linking failed. Error message: " << exc.m_error_message
<< tcu::TestLog::EndMessage;
return true;
}
/* Build process succeded */
return false;
}
/** Execute test with draw array operation
*
* @param use_version_400 Select if 400 or 420 should be used
*
* @return true if test pass, false otherwise
**/
bool NegativeTestBase::testDrawArray(bool use_version_400)
{
/* GL objects */
Utils::program program(m_context);
/* Shaders */
Utils::shaderSource fragment_data;
Utils::shaderSource geometry_data;
Utils::shaderSource tess_ctrl_data;
Utils::shaderSource tess_eval_data;
Utils::shaderSource vertex_data;
initShaderSource(Utils::FRAGMENT_SHADER, use_version_400, fragment_data);
initShaderSource(Utils::GEOMETRY_SHADER, use_version_400, geometry_data);
initShaderSource(Utils::TESS_CTRL_SHADER, use_version_400, tess_ctrl_data);
initShaderSource(Utils::TESS_EVAL_SHADER, use_version_400, tess_eval_data);
initShaderSource(Utils::VERTEX_SHADER, use_version_400, vertex_data);
/* Build program */
try
{
program.build(0 /* compute shader */, fragment_data, geometry_data, tess_ctrl_data, tess_eval_data, vertex_data,
0 /* varying names */, 0 /* n varying names */, false);
}
catch (Utils::shaderCompilationException& exc)
{
/* Compilation failed, as expected. Verify that reason of failure is as expected */
m_context.getTestContext().getLog() << tcu::TestLog::Message
<< "Shader compilation error message: " << exc.m_error_message
<< tcu::TestLog::EndMessage;
return true;
}
catch (Utils::programLinkageException& exc)
{
/* Something wrong with linking, test case failed */
m_context.getTestContext().getLog() << tcu::TestLog::Message
<< "Program linking failed. Error message: " << exc.m_error_message
<< tcu::TestLog::EndMessage;
return true;
}
/* Build process succeded */
return false;
}
/* Constants used by BindingImageTest */
const GLuint BindingImageTest::m_width = 16;
const GLuint BindingImageTest::m_green_color = 0xff00ff00;
const GLuint BindingImageTest::m_height = 16;
const GLuint BindingImageTest::m_depth = 6;
/** Constructor
*
* @param context Test context
**/
BindingImageTest::BindingImageTest(deqp::Context& context, const glw::GLchar* test_name,
const glw::GLchar* test_description)
: GLSLTestBase(context, test_name, test_description)
{
/* Nothing to be done */
}
/** Prepare buffer, filled with given color
*
* @param buffer Buffer object
* @param color Color
**/
void BindingImageTest::prepareBuffer(Utils::buffer& buffer, GLuint color)
{
std::vector<GLuint> texture_data;
texture_data.resize(m_width);
buffer.generate(GL_TEXTURE_BUFFER);
for (GLuint i = 0; i < texture_data.size(); ++i)
{
texture_data[i] = color;
}
buffer.update(m_width * sizeof(GLuint), &texture_data[0], GL_STATIC_DRAW);
}
/** Prepare texture of given type filled with given color and bind to specified image unit
*
* @param texture Texture
* @param buffer Buffer
* @param texture_type Type of texture
* @param color Color
**/
void BindingImageTest::prepareTexture(Utils::texture& texture, const Utils::buffer& buffer,
Utils::TEXTURE_TYPES texture_type, GLuint color, GLuint unit)
{
std::vector<GLuint> texture_data;
texture_data.resize(m_width * m_height * m_depth);
GLboolean is_layered = GL_FALSE;
for (GLuint i = 0; i < texture_data.size(); ++i)
{
texture_data[i] = color;
}
if (Utils::TEX_BUFFER != texture_type)
{
texture.create(m_width, m_height, m_depth, GL_RGBA8, texture_type);
texture.update(m_width, m_height, m_depth, GL_RGBA, GL_UNSIGNED_BYTE, &texture_data[0]);
}
else
{
buffer.bind();
texture.createBuffer(GL_RGBA8, buffer.m_id);
}
switch (texture_type)
{
case Utils::TEX_1D_ARRAY:
case Utils::TEX_2D_ARRAY:
case Utils::TEX_3D:
case Utils::TEX_CUBE:
is_layered = GL_TRUE;
break;
default:
break;
}
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
gl.bindImageTexture(unit, texture.m_id, 0 /* level */, is_layered /* layered */, 0 /* layer */, GL_READ_WRITE,
GL_RGBA8);
GLU_EXPECT_NO_ERROR(gl.getError(), "BindImageTexture");
}
/** Verifies that texel at offset 0 is green
*
* @param buffer Buffer object
*
* @return true if texel at offset 0 is green, false otherwise
**/
bool BindingImageTest::verifyBuffer(const Utils::buffer& buffer) const
{
GLuint* data = (GLuint*)buffer.map(GL_READ_ONLY);
GLuint color = data[0];
buffer.unmap();
return (m_green_color == color);
}
/** Verifies that texel at offset 0 is green
*
* @param buffer Buffer object
*
* @return true if texel at offset 0 is green, false otherwise
**/
bool BindingImageTest::verifyTexture(const Utils::texture& texture) const
{
static const GLuint texture_data_size = m_width * m_height * m_depth;
std::vector<glw::GLuint> texture_data;
texture_data.resize(texture_data_size);
texture.get(GL_RGBA, GL_UNSIGNED_BYTE, &texture_data[0]);
GLuint color = texture_data[0];
return (m_green_color == color);
}
/* Constants used by LineContinuationTest */
const GLuint LineContinuationTest::m_n_repetitions = 20;
const GLchar* LineContinuationTest::m_texture_coordinates_name = "texture_coordinates";
/** Constructor
*
* @param context Test context
**/
LineContinuationTest::LineContinuationTest(deqp::Context& context) : GLSLTestBase(context, "line_continuation", "desc")
{
/* Nothing to be done here */
}
/** Overwrite getShaderSourceConfig method
*
* @param out_n_parts Number of source parts used by this test case
* @param out_use_lengths If source lengths shall be provided to compiler
**/
void LineContinuationTest::getShaderSourceConfig(GLuint& out_n_parts, bool& out_use_lengths)
{
out_n_parts = (true == isShaderMultipart()) ? 2 : 1;
out_use_lengths = useSourceLengths();
}
/** Set up next test case
*
* @param test_case_index Index of next test case
*
* @return false if there is no more test cases, true otherwise
**/
bool LineContinuationTest::prepareNextTestCase(glw::GLuint test_case_index)
{
static const testCase test_cases[] = { { ASSIGNMENT_BEFORE_OPERATOR, ONCE, UNIX },
{ ASSIGNMENT_BEFORE_OPERATOR, ONCE, DOS },
{ ASSIGNMENT_BEFORE_OPERATOR, MULTIPLE_TIMES, UNIX },
{ ASSIGNMENT_BEFORE_OPERATOR, MULTIPLE_TIMES, DOS },
{ ASSIGNMENT_AFTER_OPERATOR, ONCE, UNIX },
{ ASSIGNMENT_AFTER_OPERATOR, ONCE, DOS },
{ ASSIGNMENT_AFTER_OPERATOR, MULTIPLE_TIMES, UNIX },
{ ASSIGNMENT_AFTER_OPERATOR, MULTIPLE_TIMES, DOS },
{ VECTOR_VARIABLE_INITIALIZER, ONCE, UNIX },
{ VECTOR_VARIABLE_INITIALIZER, ONCE, DOS },
{ VECTOR_VARIABLE_INITIALIZER, MULTIPLE_TIMES, UNIX },
{ VECTOR_VARIABLE_INITIALIZER, MULTIPLE_TIMES, DOS },
{ TOKEN_INSIDE_FUNCTION_NAME, ONCE, UNIX },
{ TOKEN_INSIDE_FUNCTION_NAME, ONCE, DOS },
{ TOKEN_INSIDE_FUNCTION_NAME, MULTIPLE_TIMES, UNIX },
{ TOKEN_INSIDE_FUNCTION_NAME, MULTIPLE_TIMES, DOS },
{ TOKEN_INSIDE_TYPE_NAME, ONCE, UNIX },
{ TOKEN_INSIDE_TYPE_NAME, ONCE, DOS },
{ TOKEN_INSIDE_TYPE_NAME, MULTIPLE_TIMES, UNIX },
{ TOKEN_INSIDE_TYPE_NAME, MULTIPLE_TIMES, DOS },
{ TOKEN_INSIDE_VARIABLE_NAME, ONCE, UNIX },
{ TOKEN_INSIDE_VARIABLE_NAME, ONCE, DOS },
{ TOKEN_INSIDE_VARIABLE_NAME, MULTIPLE_TIMES, UNIX },
{ TOKEN_INSIDE_VARIABLE_NAME, MULTIPLE_TIMES, DOS },
{ PREPROCESSOR_TOKEN_INSIDE, ONCE, UNIX },
{ PREPROCESSOR_TOKEN_INSIDE, ONCE, DOS },
{ PREPROCESSOR_TOKEN_INSIDE, MULTIPLE_TIMES, UNIX },
{ PREPROCESSOR_TOKEN_INSIDE, MULTIPLE_TIMES, DOS },
{ PREPROCESSOR_TOKEN_BETWEEN, ONCE, UNIX },
{ PREPROCESSOR_TOKEN_BETWEEN, ONCE, DOS },
{ PREPROCESSOR_TOKEN_BETWEEN, MULTIPLE_TIMES, UNIX },
{ PREPROCESSOR_TOKEN_BETWEEN, MULTIPLE_TIMES, DOS },
{ COMMENT, ONCE, UNIX },
{ COMMENT, ONCE, DOS },
{ COMMENT, MULTIPLE_TIMES, UNIX },
{ COMMENT, MULTIPLE_TIMES, DOS },
{ SOURCE_TERMINATION_NULL, ONCE, UNIX },
{ SOURCE_TERMINATION_NULL, ONCE, DOS },
{ SOURCE_TERMINATION_NULL, MULTIPLE_TIMES, UNIX },
{ SOURCE_TERMINATION_NULL, MULTIPLE_TIMES, DOS },
{ SOURCE_TERMINATION_NON_NULL, ONCE, UNIX },
{ SOURCE_TERMINATION_NON_NULL, ONCE, DOS },
{ SOURCE_TERMINATION_NON_NULL, MULTIPLE_TIMES, UNIX },
{ SOURCE_TERMINATION_NON_NULL, MULTIPLE_TIMES, DOS },
{ PART_TERMINATION_NULL, ONCE, UNIX },
{ PART_TERMINATION_NULL, ONCE, DOS },
{ PART_TERMINATION_NULL, MULTIPLE_TIMES, UNIX },
{ PART_TERMINATION_NULL, MULTIPLE_TIMES, DOS },
{ PART_NEXT_TO_TERMINATION_NULL, ONCE, UNIX },
{ PART_NEXT_TO_TERMINATION_NULL, ONCE, DOS },
{ PART_NEXT_TO_TERMINATION_NULL, MULTIPLE_TIMES, UNIX },
{ PART_NEXT_TO_TERMINATION_NULL, MULTIPLE_TIMES, DOS },
{ PART_TERMINATION_NON_NULL, ONCE, UNIX },
{ PART_TERMINATION_NON_NULL, ONCE, DOS },
{ PART_TERMINATION_NON_NULL, MULTIPLE_TIMES, UNIX },
{ PART_TERMINATION_NON_NULL, MULTIPLE_TIMES, DOS },
{ PART_NEXT_TO_TERMINATION_NON_NULL, ONCE, UNIX },
{ PART_NEXT_TO_TERMINATION_NON_NULL, ONCE, DOS },
{ PART_NEXT_TO_TERMINATION_NON_NULL, MULTIPLE_TIMES, UNIX },
{ PART_NEXT_TO_TERMINATION_NON_NULL, MULTIPLE_TIMES, DOS } };
static const GLuint max_test_cases = sizeof(test_cases) / sizeof(testCase);
if ((GLuint)-1 == test_case_index)
{
m_test_case.m_case = DEBUG_CASE;
}
else if (max_test_cases <= test_case_index)
{
return false;
}
else
{
m_test_case = test_cases[test_case_index];
}
m_context.getTestContext().getLog() << tcu::TestLog::Message
<< "Test case: " << repetitionsToStr((REPETITIONS)m_test_case.m_repetitions)
<< " line continuation, with "
<< lineEndingsToStr((LINE_ENDINGS)m_test_case.m_line_endings)
<< " line endings, is placed " << casesToStr((CASES)m_test_case.m_case)
<< tcu::TestLog::EndMessage;
return true;
}
/** Prepare source for given shader stage
*
* @param in_stage Shader stage, compute shader will use 430
* @param in_use_version_400 Select if 400 or 420 should be used
* @param out_source Prepared shader source instance
**/
void LineContinuationTest::prepareShaderSource(Utils::SHADER_STAGES in_stage, bool in_use_version_400,
Utils::shaderSource& out_source)
{
if (Utils::COMPUTE_SHADER == in_stage)
{
prepareComputShaderSource(out_source);
}
else
{
prepareShaderSourceForDraw(in_stage, in_use_version_400, out_source);
}
}
/** Prepare compute shader source
*
* @param source Result shader source
**/
void LineContinuationTest::prepareComputShaderSource(Utils::shaderSource& source)
{
static const GLchar* shader_template_part_0 =
"#version 430\n"
"\n"
"// Lorem ipsum dolor sit amCOMMENT_CASEet, consectetur adipiscing elit posuere.\n"
"\n"
"layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
"\n"
"/* Lorem ipsum dolor sit amet, conCOMMENT_CASEsectetur adipiscing elit posuere. */\n"
"\n"
"writeonly uniform image2D uni_image;\n"
" uniform sampler2D uni_sampler;\n"
"\n"
"void funFUNCTION_CASEction(in veTYPE_CASEc4 in_vVARIABLE_CASEalue)\n"
"{\n"
" imageStore(uni_image, ivec2(gl_GlobalInvocationID.xy), inVARIABLE_CASE_value);\n"
"}\n"
"\n"
"#define SET_PREPROCESSOR_INSIDE_CASERESULT(XX) "
"PREPROCESSOR_BETWEEN_CASEfuncFUNCTION_CASEtion(XPREPROCESSOR_INSIDE_CASEX)\n"
"NEXT_TO_TERMINATION_CASE\nTERMINATION_CASE";
static const GLchar* shader_template_part_1 =
"void main()\n"
"{\n"
" ivec2 coordinates ASSIGNMENT_BEFORE_OPERATOR_CASE=ASSIGNMENT_AFTER_OPERATOR_CASE "
"ivec2(gl_GlobalInvocationID.xy + ivec2(16, 16));\n"
" vec4 sampled_color = texelFetch(uni_sampler, coordinates, 0 /* lod */);\n"
" vec4 result = vec4(0, 0VECTOR_VARIABLE_INITIALIZER_CASE, 0, 1);\n"
"\n"
" if (vec4(0, 0, 1, 1) == sampled_color)\n"
" {\n"
" result = vecTYPE_CASE4(VECTOR_VARIABLE_INITIALIZER_CASE0, 1, 0, 1);\n"
" }\n"
" else\n"
" {\n"
" result = vec4(coordinates.xy, sampled_color.rg);\n"
" }\n"
"\n"
" SET_RESULT(result);"
"}\n";
/* Init strings with templates and replace all CASE tokens */
if (true == isShaderMultipart())
{
source.m_parts[0].m_code = shader_template_part_0;
source.m_parts[1].m_code = shader_template_part_1;
replaceAllCaseTokens(source.m_parts[0].m_code);
replaceAllCaseTokens(source.m_parts[1].m_code);
}
else
{
source.m_parts[0].m_code = shader_template_part_0;
source.m_parts[0].m_code.append(shader_template_part_1);
replaceAllCaseTokens(source.m_parts[0].m_code);
}
}
/** Prepare source for given shader stage
*
* @param stage Shader stage, compute shader will use 430
* @param use_version_400 Select if 400 or 420 should be used
* @param source Result shader sources
**/
void LineContinuationTest::prepareShaderSourceForDraw(Utils::SHADER_STAGES stage, bool use_version_400,
Utils::shaderSource& source)
{
/* Templates */
static const GLchar* shader_template_part_0 =
"VERSION\n"
"\n"
"// Lorem ipsum dolor sit amCOMMENT_CASEet, consectetur adipiscing elit posuere.\n"
"\n"
"STAGE_SPECIFIC\n"
"\n"
"/* Lorem ipsum dolor sit amet, conCOMMENT_CASEsectetur adipiscing elit posuere. */\n"
"\n"
"IN_COLOR_DEFINITION\n"
"IN_TEXTURE_COORDINATES_DEFINITION\n"
"OUT_COLOR_DEFINITION\n"
"OUT_TEXTURE_COORDINATES_DEFINITION\n"
"uniform sampler2D uni_sampler;\n"
"\n"
"void funFUNCTION_CASEction(in veTYPE_CASEc4 in_vVARIABLE_CASEalue)\n"
"{\n"
" OUT_COLOR ASSIGNMENT_BEFORE_OPERATOR_CASE=ASSIGNMENT_AFTER_OPERATOR_CASE inVARIABLE_CASE_value;\n"
"}\n"
"\n"
"#define SET_PREPROCESSOR_INSIDE_CASERESULT(XX) "
"PREPROCESSOR_BETWEEN_CASEfuncFUNCTION_CASEtion(XPREPROCESSOR_INSIDE_CASEX)\n"
"NEXT_TO_TERMINATION_CASE\nTERMINATION_CASE";
static const GLchar* shader_template_part_1 =
"void main()\n"
"{\n"
" vec2 coordinates = TEXTURE_COORDINATES;\n"
" vec4 sampled_color = texture(uni_sampler, coordinates);\n"
" vec4 result = vec4(0, 0VECTOR_VARIABLE_INITIALIZER_CASE, 0, 1);\n"
"\n"
" if (PASS_CONDITION)\n"
" {\n"
" result = vecTYPE_CASE4(VECTOR_VARIABLE_INITIALIZER_CASE0, 1, 0, 1);\n"
" }\n"
" else\n"
" {\n"
" result = vec4(coordinates.xy, sampled_color.rg);\n"
" }\n"
"\n"
"STORE_RESULTS"
"}\n"
"NEXT_TO_TERMINATION_CASE\nTERMINATION_CASE";
static const GLchar* store_results_template = " SET_RESULT(result);\n"
" TEXTURE_COORDINATES = coordinates;\n";
static const GLchar* store_results_tcs_template = " SET_RESULT(result);\n"
" TEXTURE_COORDINATES = coordinates;\n"
" gl_TessLevelOuter[0] = 1.0;\n"
" gl_TessLevelOuter[1] = 1.0;\n"
" gl_TessLevelOuter[2] = 1.0;\n"
" gl_TessLevelOuter[3] = 1.0;\n"
" gl_TessLevelInner[0] = 1.0;\n"
" gl_TessLevelInner[1] = 1.0;\n";
static const GLchar* store_results_fs_template = " SET_RESULT(result);\n";
static const GLchar* store_results_gs_template = " gl_Position = vec4(-1, -1, 0, 1);\n"
" SET_RESULT(result);\n"
" TEXTURE_COORDINATES = coordinates + vec2(-0.25, -0.25);\n"
" EmitVertex();\n"
" gl_Position = vec4(-1, 1, 0, 1);\n"
" SET_RESULT(result);\n"
" TEXTURE_COORDINATES = coordinates + vec2(-0.25, 0.25);\n"
" EmitVertex();\n"
" gl_Position = vec4(1, -1, 0, 1);\n"
" SET_RESULT(result);\n"
" TEXTURE_COORDINATES = coordinates + vec2(0.25, -0.25);\n"
" EmitVertex();\n"
" gl_Position = vec4(1, 1, 0, 1);\n"
" SET_RESULT(result);\n"
" TEXTURE_COORDINATES = coordinates + vec2(0.25, 0.25);\n"
" EmitVertex();\n";
static const GLchar* pass_condition_template = "(EXPECTED_VALUE == sampled_color) &&\n"
" (vec4(0, 1, 0, 1) == IN_COLOR) ";
static const GLchar* pass_condition_vs_template = "EXPECTED_VALUE == sampled_color";
/* Tokens to be replaced with GLSL stuff */
static const GLchar* token_version = "VERSION";
static const GLchar* token_stage_specific = "STAGE_SPECIFIC";
static const GLchar* token_in_color_definition = "IN_COLOR_DEFINITION";
static const GLchar* token_in_tex_coord_definition = "IN_TEXTURE_COORDINATES_DEFINITION";
static const GLchar* token_out_color_definition = "OUT_COLOR_DEFINITION";
static const GLchar* token_out_tex_coord_definition = "OUT_TEXTURE_COORDINATES_DEFINITION";
static const GLchar* token_expected_value = "EXPECTED_VALUE";
static const GLchar* token_texture_coordinates = "TEXTURE_COORDINATES";
static const GLchar* token_in_color = "IN_COLOR";
static const GLchar* token_out_color = "OUT_COLOR";
static const GLchar* token_store_results = "STORE_RESULTS";
static const GLchar* token_pass_condition = "PASS_CONDITION";
/* Name of variable and empty string*/
static const GLchar* color_name = "color";
static const GLchar* empty = "";
/* GLSL stuff */
const GLchar* version = getVersionString(stage, use_version_400);
const GLchar* stage_specific_layout = getStageSpecificLayout(stage);
const GLchar* expected_value = getExpectedValueString();
/* Qualifiers */
Utils::qualifierSet in;
Utils::qualifierSet out;
in.push_back(Utils::QUAL_IN);
out.push_back(Utils::QUAL_OUT);
/* In/Out variables definitions and references */
std::string in_tex_coord_reference;
std::string out_tex_coord_reference;
std::string in_color_reference;
std::string out_color_reference;
std::string in_tex_coord_definition;
std::string out_tex_coord_definition;
std::string in_color_definition;
std::string out_color_definition;
Utils::prepareVariableStrings(stage, Utils::INPUT, in, "vec2", m_texture_coordinates_name, in_tex_coord_definition,
in_tex_coord_reference);
Utils::prepareVariableStrings(stage, Utils::OUTPUT, out, "vec2", m_texture_coordinates_name,
out_tex_coord_definition, out_tex_coord_reference);
Utils::prepareVariableStrings(stage, Utils::INPUT, in, "vec4", color_name, in_color_definition, in_color_reference);
Utils::prepareVariableStrings(stage, Utils::OUTPUT, out, "vec4", color_name, out_color_definition,
out_color_reference);
in_tex_coord_definition.append(";");
out_tex_coord_definition.append(";");
in_color_definition.append(";");
out_color_definition.append(";");
/* Select pass condition and store results tempaltes */
const GLchar* store_results = store_results_template;
const GLchar* pass_condition = pass_condition_template;
switch (stage)
{
case Utils::FRAGMENT_SHADER:
store_results = store_results_fs_template;
break;
case Utils::GEOMETRY_SHADER:
store_results = store_results_gs_template;
break;
case Utils::TESS_CTRL_SHADER:
store_results = store_results_tcs_template;
break;
case Utils::VERTEX_SHADER:
pass_condition = pass_condition_vs_template;
break;
default:
break;
};
const GLuint store_results_length = static_cast<GLuint>(strlen(store_results));
const GLuint pass_condition_length = static_cast<GLuint>(strlen(pass_condition));
/* Init strings with templates and replace all CASE tokens */
if (true == isShaderMultipart())
{
source.m_parts[0].m_code = shader_template_part_0;
source.m_parts[1].m_code = shader_template_part_1;
replaceAllCaseTokens(source.m_parts[0].m_code);
replaceAllCaseTokens(source.m_parts[1].m_code);
}
else
{
source.m_parts[0].m_code = shader_template_part_0;
source.m_parts[0].m_code.append(shader_template_part_1);
replaceAllCaseTokens(source.m_parts[0].m_code);
}
/* Get memory for shader source parts */
const bool is_multipart = isShaderMultipart();
size_t position = 0;
std::string& shader_source_part_0 = source.m_parts[0].m_code;
std::string& shader_source_part_1 = (true == is_multipart) ? source.m_parts[1].m_code : source.m_parts[0].m_code;
/* Replace tokens */
/* Part 0 */
Utils::replaceToken(token_version, position, version, shader_source_part_0);
Utils::replaceToken(token_stage_specific, position, stage_specific_layout, shader_source_part_0);
if (Utils::VERTEX_SHADER != stage)
{
Utils::replaceToken(token_in_color_definition, position, in_color_definition.c_str(), shader_source_part_0);
}
else
{
Utils::replaceToken(token_in_color_definition, position, empty, shader_source_part_0);
}
Utils::replaceToken(token_in_tex_coord_definition, position, in_tex_coord_definition.c_str(), shader_source_part_0);
Utils::replaceToken(token_out_color_definition, position, out_color_definition.c_str(), shader_source_part_0);
if (Utils::FRAGMENT_SHADER == stage)
{
Utils::replaceToken(token_out_tex_coord_definition, position, empty, shader_source_part_0);
}
else
{
Utils::replaceToken(token_out_tex_coord_definition, position, out_tex_coord_definition.c_str(),
shader_source_part_0);
}
Utils::replaceToken(token_out_color, position, out_color_reference.c_str(), shader_source_part_0);
/* Part 1 */
if (true == is_multipart)
{
position = 0;
}
Utils::replaceToken(token_texture_coordinates, position, in_tex_coord_reference.c_str(), shader_source_part_1);
Utils::replaceToken(token_pass_condition, position, pass_condition, shader_source_part_1);
position -= pass_condition_length;
Utils::replaceToken(token_expected_value, position, expected_value, shader_source_part_1);
if (Utils::VERTEX_SHADER != stage)
{
Utils::replaceToken(token_in_color, position, in_color_reference.c_str(), shader_source_part_1);
}
Utils::replaceToken(token_store_results, position, store_results, shader_source_part_1);
position -= store_results_length;
if (Utils::GEOMETRY_SHADER == stage)
{
for (GLuint i = 0; i < 4; ++i)
{
Utils::replaceToken(token_texture_coordinates, position, out_tex_coord_reference.c_str(),
shader_source_part_1);
}
}
else if (Utils::FRAGMENT_SHADER == stage)
{
/* Nothing to be done */
}
else
{
Utils::replaceToken(token_texture_coordinates, position, out_tex_coord_reference.c_str(), shader_source_part_1);
}
}
/** Prepare texture
*
* @param texture Texutre to be created and filled with content
*
* @return Name of sampler uniform that should be used for the texture
**/
const GLchar* LineContinuationTest::prepareSourceTexture(Utils::texture& texture)
{
std::vector<GLuint> data;
static const GLuint width = 64;
static const GLuint height = 64;
static const GLuint data_size = width * height;
static const GLuint blue_color = 0xffff0000;
static const GLuint grey_color = 0xaaaaaaaa;
data.resize(data_size);
for (GLuint i = 0; i < data_size; ++i)
{
data[i] = grey_color;
}
for (GLuint y = 16; y < 48; ++y)
{
const GLuint line_offset = y * 64;
for (GLuint x = 16; x < 48; ++x)
{
const GLuint pixel_offset = x + line_offset;
data[pixel_offset] = blue_color;
}
}
texture.create(width, height, GL_RGBA8);
texture.update(width, height, 0 /* depth */, GL_RGBA, GL_UNSIGNED_BYTE, &data[0]);
return "uni_sampler";
}
/** Prepare vertex buffer, vec2 tex_coord
*
* @param program Program object
* @param buffer Vertex buffer
* @param vao Vertex array object
**/
void LineContinuationTest::prepareVertexBuffer(const Utils::program& program, Utils::buffer& buffer,
Utils::vertexArray& vao)
{
std::string tex_coord_name = Utils::getVariableName(Utils::VERTEX_SHADER, Utils::INPUT, m_texture_coordinates_name);
GLint tex_coord_loc = program.getAttribLocation(tex_coord_name.c_str());
if (-1 == tex_coord_loc)
{
TCU_FAIL("Vertex attribute location is invalid");
}
vao.generate();
vao.bind();
buffer.generate(GL_ARRAY_BUFFER);
GLfloat data[] = { 0.5f, 0.5f, 0.5f, 0.5f };
GLsizeiptr data_size = sizeof(data);
buffer.update(data_size, data, GL_STATIC_DRAW);
/* GL entry points */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Set up vao */
gl.vertexAttribPointer(tex_coord_loc, 2 /* size */, GL_FLOAT /* type */, GL_FALSE /* normalized*/, 0 /* stride */,
0 /* offset */);
GLU_EXPECT_NO_ERROR(gl.getError(), "VertexAttribPointer");
/* Enable attribute */
gl.enableVertexAttribArray(tex_coord_loc);
GLU_EXPECT_NO_ERROR(gl.getError(), "EnableVertexAttribArray");
}
/** Get string describing test cases
*
* @param cases Test case
*
* @return String describing current test case
**/
const GLchar* LineContinuationTest::casesToStr(CASES cases) const
{
const GLchar* result = 0;
switch (cases)
{
case ASSIGNMENT_BEFORE_OPERATOR:
result = "just before assignment operator";
break;
case ASSIGNMENT_AFTER_OPERATOR:
result = "just after assignment operator";
break;
case VECTOR_VARIABLE_INITIALIZER:
result = "inside vector variable initializer";
break;
case TOKEN_INSIDE_FUNCTION_NAME:
result = "inside function name";
break;
case TOKEN_INSIDE_TYPE_NAME:
result = "inside type name";
break;
case TOKEN_INSIDE_VARIABLE_NAME:
result = "inside variable name";
break;
case PREPROCESSOR_TOKEN_INSIDE:
result = "inside preprocessor token";
break;
case PREPROCESSOR_TOKEN_BETWEEN:
result = "between preprocessor token";
break;
case COMMENT:
result = "inside comment";
break;
case SOURCE_TERMINATION_NULL:
result = "just before null terminating source";
break;
case SOURCE_TERMINATION_NON_NULL:
result = "as last character in source string, without null termination";
break;
case PART_TERMINATION_NULL:
result = "just before null terminating part of source";
break;
case PART_NEXT_TO_TERMINATION_NULL:
result = "just before last character in part of source";
break;
case PART_TERMINATION_NON_NULL:
result = "as last character in part string, without null termination";
break;
case PART_NEXT_TO_TERMINATION_NON_NULL:
result = "just before last character in part string, without null termination";
break;
case DEBUG_CASE: /* intended fall through */
default:
result = "nowhere at all. This is debug!";
break;
}
return result;
}
/** Get expected value, blue color as vec4
*
* @return blue color
**/
const GLchar* LineContinuationTest::getExpectedValueString() const
{
return "vec4(0, 0, 1, 1)";
}
/** Get line continuation string, single or multiple \
*
* @return String
**/
std::string LineContinuationTest::getLineContinuationString() const
{
static const GLchar line_continuation_ending_dos[] = { '\\', 0x0d, 0x0a, 0x00 };
static const GLchar line_continuation_ending_unix[] = { '\\', 0x0a, 0x00 };
std::string result;
const GLchar* selected_string;
if (DOS == m_test_case.m_line_endings)
{
selected_string = line_continuation_ending_dos;
}
else
{
selected_string = line_continuation_ending_unix;
}
GLuint n_repetitions = (ONCE == m_test_case.m_repetitions) ? 1 : m_n_repetitions;
for (GLuint i = 0; i < n_repetitions; ++i)
{
result.append(selected_string);
}
return result;
}
/** Decides if shader should consist of multiple parts for the current test case
*
* @return true if test case requires multiple parts, false otherwise
**/
bool LineContinuationTest::isShaderMultipart() const
{
bool result;
switch (m_test_case.m_case)
{
case ASSIGNMENT_BEFORE_OPERATOR:
case ASSIGNMENT_AFTER_OPERATOR:
case VECTOR_VARIABLE_INITIALIZER:
case TOKEN_INSIDE_FUNCTION_NAME:
case TOKEN_INSIDE_TYPE_NAME:
case TOKEN_INSIDE_VARIABLE_NAME:
case PREPROCESSOR_TOKEN_INSIDE:
case PREPROCESSOR_TOKEN_BETWEEN:
case COMMENT:
case SOURCE_TERMINATION_NULL:
case SOURCE_TERMINATION_NON_NULL:
default:
result = false;
break;
case PART_TERMINATION_NULL:
case PART_NEXT_TO_TERMINATION_NULL:
case PART_TERMINATION_NON_NULL:
case PART_NEXT_TO_TERMINATION_NON_NULL:
result = true;
break;
};
return result;
}
/** String describing line endings
*
* @param line_ending Line ending enum
*
* @return "unix" or "dos" strings
**/
const GLchar* LineContinuationTest::lineEndingsToStr(LINE_ENDINGS line_ending) const
{
const GLchar* result = 0;
if (UNIX == line_ending)
{
result = "unix";
}
else
{
result = "dos";
}
return result;
}
/** String describing number of repetitions
*
* @param repetitions Repetitions enum
*
* @return "single" or "multiple" strings
**/
const GLchar* LineContinuationTest::repetitionsToStr(REPETITIONS repetitions) const
{
const GLchar* result = 0;
if (ONCE == repetitions)
{
result = "single";
}
else
{
result = "multiple";
}
return result;
}
/** Replace all CASES tokens
*
* @param source String with shader template
**/
void LineContinuationTest::replaceAllCaseTokens(std::string& source) const
{
/* Tokens to be replaced with line continuation */
static const GLchar* token_assignment_before_operator_case = "ASSIGNMENT_BEFORE_OPERATOR_CASE";
static const GLchar* token_assignment_after_operator_case = "ASSIGNMENT_AFTER_OPERATOR_CASE";
static const