blob: 06d8e6c613c5b8a99500bbc88cdb30bedfa5e571 [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 gl4cShaderSubroutineTests.cpp
* \brief Implements conformance tests for "Shader Subroutine" functionality.
*/ /*-------------------------------------------------------------------*/
#include "gl4cShaderSubroutineTests.hpp"
#include "gluContextInfo.hpp"
#include "glwEnums.hpp"
#include "glwFunctions.hpp"
#include "tcuMatrix.hpp"
#include <cmath>
#include <cstring>
#include <deMath.h>
using namespace glw;
namespace gl4cts
{
namespace ShaderSubroutine
{
/** Constructor.
*
* @param context CTS context.
**/
Utils::buffer::buffer(deqp::Context& context) : m_id(0), m_context(context)
{
}
/** Destructor
*
**/
Utils::buffer::~buffer()
{
if (0 != m_id)
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
gl.deleteBuffers(1, &m_id);
m_id = 0;
}
}
/** Execute BindBufferRange
*
* @param target <target> parameter
* @param index <index> parameter
* @param offset <offset> parameter
* @param size <size> parameter
**/
void Utils::buffer::bindRange(glw::GLenum target, glw::GLuint index, glw::GLintptr offset, glw::GLsizeiptr size)
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
gl.bindBufferRange(target, index, m_id, offset, size);
GLU_EXPECT_NO_ERROR(gl.getError(), "BindBufferRange");
}
/** Execute GenBuffer
*
**/
void Utils::buffer::generate()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
gl.genBuffers(1, &m_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "GenBuffers");
}
/** Execute BufferData
*
* @param target <target> parameter
* @param size <size> parameter
* @param data <data> parameter
* @param usage <usage> parameter
**/
void Utils::buffer::update(glw::GLenum target, glw::GLsizeiptr size, glw::GLvoid* data, glw::GLenum usage)
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
gl.bindBuffer(target, m_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "bindBuffer");
gl.bufferData(target, size, data, usage);
GLU_EXPECT_NO_ERROR(gl.getError(), "bufferData");
}
/** Constructor
*
* @param context CTS context
**/
Utils::framebuffer::framebuffer(deqp::Context& context) : m_id(0), m_context(context)
{
/* Nothing to be done here */
}
/** Destructor
*
**/
Utils::framebuffer::~framebuffer()
{
if (0 != m_id)
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
gl.deleteFramebuffers(1, &m_id);
m_id = 0;
}
}
/** Attach texture to specified attachment
*
* @param attachment Attachment
* @param texture_id Texture id
* @param width Texture width
* @param height Texture height
**/
void Utils::framebuffer::attachTexture(glw::GLenum attachment, glw::GLuint texture_id, glw::GLuint width,
glw::GLuint height)
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
bind();
gl.bindTexture(GL_TEXTURE_2D, texture_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "BindTexture");
gl.framebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, GL_TEXTURE_2D, texture_id, 0 /* level */);
GLU_EXPECT_NO_ERROR(gl.getError(), "FramebufferTexture2D");
gl.viewport(0 /* x */, 0 /* y */, width, height);
GLU_EXPECT_NO_ERROR(gl.getError(), "Viewport");
}
/** Binds framebuffer to DRAW_FRAMEBUFFER
*
**/
void Utils::framebuffer::bind()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, m_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "BindFramebuffer");
}
/** Clear framebuffer
*
* @param mask <mask> parameter of glClear. Decides which shall be cleared
**/
void Utils::framebuffer::clear(glw::GLenum mask)
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
gl.clear(mask);
GLU_EXPECT_NO_ERROR(gl.getError(), "Clear");
}
/** Specifie clear color
*
* @param red Red channel
* @param green Green channel
* @param blue Blue channel
* @param alpha Alpha channel
**/
void Utils::framebuffer::clearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
gl.clearColor(red, green, blue, alpha);
GLU_EXPECT_NO_ERROR(gl.getError(), "ClearColor");
}
/** Generate framebuffer
*
**/
void Utils::framebuffer::generate()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
gl.genFramebuffers(1, &m_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "GenFramebuffers");
}
const glw::GLenum Utils::program::ARB_COMPUTE_SHADER = 0x91B9;
/** Constructor.
*
* @param context CTS context.
**/
Utils::program::program(deqp::Context& context)
: m_compute_shader_id(0)
, m_fragment_shader_id(0)
, m_geometry_shader_id(0)
, m_program_object_id(0)
, m_tesselation_control_shader_id(0)
, m_tesselation_evaluation_shader_id(0)
, m_vertex_shader_id(0)
, m_context(context)
{
/* Nothing to be done here */
}
/** Destructor
*
**/
Utils::program::~program()
{
remove();
}
/** Build program
*
* @param compute_shader_code Compute shader source code
* @param fragment_shader_code Fragment shader source code
* @param geometry_shader_code Geometry shader source code
* @param tesselation_control_shader_code Tesselation control shader source code
* @param tesselation_evaluation_shader_code Tesselation evaluation shader source code
* @param vertex_shader_code Vertex shader source code
* @param varying_names Array of strings containing names of varyings to be captured with transfrom feedback
* @param n_varying_names Number of varyings to be captured with transfrom feedback
* @param is_separable Selects if monolithis or separable program should be built. Defaults to false
**/
void Utils::program::build(const glw::GLchar* compute_shader_code, const glw::GLchar* fragment_shader_code,
const glw::GLchar* geometry_shader_code, const glw::GLchar* tesselation_control_shader_code,
const glw::GLchar* tesselation_evaluation_shader_code, const glw::GLchar* vertex_shader_code,
const glw::GLchar* const* varying_names, glw::GLuint n_varying_names, bool is_separable)
{
/* GL entry points */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Create shader objects and compile */
if (0 != compute_shader_code)
{
m_compute_shader_id = gl.createShader(ARB_COMPUTE_SHADER);
GLU_EXPECT_NO_ERROR(gl.getError(), "CreateShader");
compile(m_compute_shader_id, compute_shader_code);
}
if (0 != fragment_shader_code)
{
m_fragment_shader_id = gl.createShader(GL_FRAGMENT_SHADER);
GLU_EXPECT_NO_ERROR(gl.getError(), "CreateShader");
compile(m_fragment_shader_id, fragment_shader_code);
}
if (0 != geometry_shader_code)
{
m_geometry_shader_id = gl.createShader(GL_GEOMETRY_SHADER);
GLU_EXPECT_NO_ERROR(gl.getError(), "CreateShader");
compile(m_geometry_shader_id, geometry_shader_code);
}
if (0 != tesselation_control_shader_code)
{
m_tesselation_control_shader_id = gl.createShader(GL_TESS_CONTROL_SHADER);
GLU_EXPECT_NO_ERROR(gl.getError(), "CreateShader");
compile(m_tesselation_control_shader_id, tesselation_control_shader_code);
}
if (0 != tesselation_evaluation_shader_code)
{
m_tesselation_evaluation_shader_id = gl.createShader(GL_TESS_EVALUATION_SHADER);
GLU_EXPECT_NO_ERROR(gl.getError(), "CreateShader");
compile(m_tesselation_evaluation_shader_id, tesselation_evaluation_shader_code);
}
if (0 != vertex_shader_code)
{
m_vertex_shader_id = gl.createShader(GL_VERTEX_SHADER);
GLU_EXPECT_NO_ERROR(gl.getError(), "CreateShader");
compile(m_vertex_shader_id, vertex_shader_code);
}
/* Create program object */
m_program_object_id = gl.createProgram();
GLU_EXPECT_NO_ERROR(gl.getError(), "CreateProgram");
/* Set up captyured varyings' names */
if (0 != n_varying_names)
{
gl.transformFeedbackVaryings(m_program_object_id, n_varying_names, varying_names, GL_INTERLEAVED_ATTRIBS);
GLU_EXPECT_NO_ERROR(gl.getError(), "TransformFeedbackVaryings");
}
/* Set separable parameter */
if (true == is_separable)
{
gl.programParameteri(m_program_object_id, GL_PROGRAM_SEPARABLE, GL_TRUE);
GLU_EXPECT_NO_ERROR(gl.getError(), "ProgramParameteri");
}
/* Link program */
link();
}
/** Compile shader
*
* @param shader_id Shader object id
* @param shader_code Shader source code
**/
void Utils::program::compile(glw::GLuint shader_id, const glw::GLchar* shader_code) const
{
/* GL entry points */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Compilation status */
glw::GLint status = GL_FALSE;
/* Set source code */
gl.shaderSource(shader_id, 1 /* count */, &shader_code, 0);
GLU_EXPECT_NO_ERROR(gl.getError(), "ShaderSource");
/* Compile */
gl.compileShader(shader_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "CompileShader");
/* Get compilation status */
gl.getShaderiv(shader_id, GL_COMPILE_STATUS, &status);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetShaderiv");
/* Log compilation error */
if (GL_TRUE != status)
{
glw::GLint length = 0;
std::vector<glw::GLchar> message;
/* Error log length */
gl.getShaderiv(shader_id, GL_INFO_LOG_LENGTH, &length);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetShaderiv");
/* Prepare storage */
message.resize(length);
/* Get error log */
gl.getShaderInfoLog(shader_id, length, 0, &message[0]);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetShaderInfoLog");
/* Log */
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Failed to compile shader:\n"
<< &message[0] << "\nShader source\n"
<< shader_code << tcu::TestLog::EndMessage;
TCU_FAIL("Failed to compile shader");
}
}
/** Checks whether the tested driver supports GL_ARB_get_program_binary
*
* @return true if the extension is supported and, also, at least one binary format.
**/
bool Utils::program::isProgramBinarySupported() const
{
glw::GLint n_program_binary_formats = 0;
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
if (m_context.getContextInfo().isExtensionSupported("GL_ARB_get_program_binary"))
{
gl.getIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &n_program_binary_formats);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() call failed.");
}
return n_program_binary_formats > 0;
}
/** Create program from provided binary
*
* @param binary Buffer with binary form of program
* @param binary_format Format of <binary> data
**/
void Utils::program::createFromBinary(const std::vector<GLubyte>& binary, GLenum binary_format)
{
/* GL entry points */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Create program object */
m_program_object_id = gl.createProgram();
GLU_EXPECT_NO_ERROR(gl.getError(), "CreateProgram");
gl.programBinary(m_program_object_id, binary_format, &binary[0], (GLsizei)binary.size());
GLU_EXPECT_NO_ERROR(gl.getError(), "ProgramBinary");
}
/** Get binary form of program
*
* @param binary Buffer for binary data
* @param binary_format Format of binary data
**/
void Utils::program::getBinary(std::vector<GLubyte>& binary, GLenum& binary_format) const
{
/* GL entry points */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Get binary size */
GLint length = 0;
gl.getProgramiv(m_program_object_id, GL_PROGRAM_BINARY_LENGTH, &length);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramiv");
/* Allocate storage */
binary.resize(length);
/* Get binary */
gl.getProgramBinary(m_program_object_id, (GLsizei)binary.size(), &length, &binary_format, &binary[0]);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramBinary");
}
/** Get subroutine index
*
* @param subroutine_name Subroutine name
*
* @return Index of subroutine
**/
GLuint Utils::program::getSubroutineIndex(const glw::GLchar* subroutine_name, glw::GLenum shader_stage) const
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
GLuint index = -1;
index = gl.getSubroutineIndex(m_program_object_id, shader_stage, subroutine_name);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetSubroutineIndex");
if (GL_INVALID_INDEX == index)
{
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Subroutine: " << subroutine_name
<< " is not available" << tcu::TestLog::EndMessage;
TCU_FAIL("Subroutine is not available");
}
return index;
}
/** Get subroutine uniform location
*
* @param uniform_name Subroutine uniform name
*
* @return Location of subroutine uniform
**/
GLint Utils::program::getSubroutineUniformLocation(const glw::GLchar* uniform_name, glw::GLenum shader_stage) const
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
GLint location = -1;
location = gl.getSubroutineUniformLocation(m_program_object_id, shader_stage, uniform_name);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetSubroutineUniformLocation");
if (-1 == location)
{
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Subroutine uniform: " << uniform_name
<< " is not available" << tcu::TestLog::EndMessage;
TCU_FAIL("Subroutine uniform is not available");
}
return location;
}
/** Get uniform location
*
* @param uniform_name Subroutine uniform name
*
* @return Location of uniform
**/
GLint Utils::program::getUniformLocation(const glw::GLchar* uniform_name) const
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
GLint location = -1;
location = gl.getUniformLocation(m_program_object_id, uniform_name);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetUniformLocation");
if (-1 == location)
{
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Uniform: " << uniform_name
<< " is not available" << tcu::TestLog::EndMessage;
TCU_FAIL("Uniform is not available");
}
return location;
}
/** Attach shaders and link program
*
**/
void Utils::program::link() const
{
/* GL entry points */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Link status */
glw::GLint status = GL_FALSE;
/* Attach shaders */
if (0 != m_compute_shader_id)
{
gl.attachShader(m_program_object_id, m_compute_shader_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "AttachShader");
}
if (0 != m_fragment_shader_id)
{
gl.attachShader(m_program_object_id, m_fragment_shader_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "AttachShader");
}
if (0 != m_geometry_shader_id)
{
gl.attachShader(m_program_object_id, m_geometry_shader_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "AttachShader");
}
if (0 != m_tesselation_control_shader_id)
{
gl.attachShader(m_program_object_id, m_tesselation_control_shader_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "AttachShader");
}
if (0 != m_tesselation_evaluation_shader_id)
{
gl.attachShader(m_program_object_id, m_tesselation_evaluation_shader_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "AttachShader");
}
if (0 != m_vertex_shader_id)
{
gl.attachShader(m_program_object_id, m_vertex_shader_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "AttachShader");
}
/* Link */
gl.linkProgram(m_program_object_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "LinkProgram");
/* Get link status */
gl.getProgramiv(m_program_object_id, GL_LINK_STATUS, &status);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramiv");
/* Log link error */
if (GL_TRUE != status)
{
glw::GLint length = 0;
std::vector<glw::GLchar> message;
/* Get error log length */
gl.getProgramiv(m_program_object_id, GL_INFO_LOG_LENGTH, &length);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramiv");
message.resize(length);
/* Get error log */
gl.getProgramInfoLog(m_program_object_id, length, 0, &message[0]);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramInfoLog");
/* Log */
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Failed to link program:\n"
<< &message[0] << tcu::TestLog::EndMessage;
TCU_FAIL("Failed to link program");
}
}
/** Delete program object and all attached shaders
*
**/
void Utils::program::remove()
{
/* GL entry points */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Make sure program object is no longer used by GL */
gl.useProgram(0);
/* Clean program object */
if (0 != m_program_object_id)
{
gl.deleteProgram(m_program_object_id);
m_program_object_id = 0;
}
/* Clean shaders */
if (0 != m_compute_shader_id)
{
gl.deleteShader(m_compute_shader_id);
m_compute_shader_id = 0;
}
if (0 != m_fragment_shader_id)
{
gl.deleteShader(m_fragment_shader_id);
m_fragment_shader_id = 0;
}
if (0 != m_geometry_shader_id)
{
gl.deleteShader(m_geometry_shader_id);
m_geometry_shader_id = 0;
}
if (0 != m_tesselation_control_shader_id)
{
gl.deleteShader(m_tesselation_control_shader_id);
m_tesselation_control_shader_id = 0;
}
if (0 != m_tesselation_evaluation_shader_id)
{
gl.deleteShader(m_tesselation_evaluation_shader_id);
m_tesselation_evaluation_shader_id = 0;
}
if (0 != m_vertex_shader_id)
{
gl.deleteShader(m_vertex_shader_id);
m_vertex_shader_id = 0;
}
}
/** Execute UseProgram
*
**/
void Utils::program::use() const
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
gl.useProgram(m_program_object_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "UseProgram");
}
/** Constructor.
*
* @param context CTS context.
**/
Utils::texture::texture(deqp::Context& context) : m_id(0), m_context(context)
{
/* Nothing to done here */
}
/** Destructor
*
**/
Utils::texture::~texture()
{
if (0 != m_id)
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
gl.deleteTextures(1, &m_id);
m_id = 0;
}
}
/** Bind texture to GL_TEXTURE_2D
*
**/
void Utils::texture::bind()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
gl.bindTexture(GL_TEXTURE_2D, m_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "BindTexture");
}
/** Create 2d texture
*
* @param width Width of texture
* @param height Height of texture
* @param internal_format Internal format of texture
**/
void Utils::texture::create(glw::GLuint width, glw::GLuint height, glw::GLenum internal_format)
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
gl.genTextures(1, &m_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "GenTextures");
bind();
gl.texStorage2D(GL_TEXTURE_2D, 1 /* levels */, internal_format, width, height);
GLU_EXPECT_NO_ERROR(gl.getError(), "TexStorage2D");
}
/** Get contents of texture
*
* @param format Format of image
* @param type Type of image
* @param out_data Buffer for image
**/
void Utils::texture::get(glw::GLenum format, glw::GLenum type, glw::GLvoid* out_data)
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
bind();
gl.getTexImage(GL_TEXTURE_2D, 0, format, type, out_data);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetTexImage");
}
/** Update contents of texture
*
* @param width Width of texture
* @param height Height of texture
* @param format Format of data
* @param type Type of data
* @param data Buffer with image
**/
void Utils::texture::update(glw::GLuint width, glw::GLuint height, glw::GLenum format, glw::GLenum type,
glw::GLvoid* data)
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
bind();
gl.texSubImage2D(GL_TEXTURE_2D, 0 /* level */, 0 /* x */, 0 /* y */, width, height, format, type, data);
GLU_EXPECT_NO_ERROR(gl.getError(), "TexSubImage2D");
}
/** Constructor.
*
* @param context CTS context.
**/
Utils::vertexArray::vertexArray(deqp::Context& context) : m_id(0), m_context(context)
{
}
/** Destructor
*
**/
Utils::vertexArray::~vertexArray()
{
if (0 != m_id)
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
gl.deleteVertexArrays(1, &m_id);
m_id = 0;
}
}
/** Execute BindVertexArray
*
**/
void Utils::vertexArray::bind()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
gl.bindVertexArray(m_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "BindVertexArray");
}
/** Execute GenVertexArrays
*
**/
void Utils::vertexArray::generate()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
gl.genVertexArrays(1, &m_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "GenVertexArrays");
}
/** Builds a program object consisting of up to 5 shader stages
* (vertex/tessellation control/tessellation evaluation/geometry/fragment).
* The shaders are attached to the program object, then compiled. Finally,
* the program object is linked.
*
* XFB can be optionally configured for the program object.
*
* Should an error be reported by GL implementation, a TestError
* exception will be thrown.
*
* @param gl OpenGL functions from the active rendering context.
* @param vs_body Body to use for the vertex shader. Can be an empty string.
* @param tc_body Body to use for the tessellation control shader. Can be
* an empty string.
* @param te_body Body to use for the tessellation evaluation shader. Can be
* an empty string.
* @param gs_body Body to use for the geometry shader. Can be an empty string.
* @param fs_body Body to use for the fragment shader. Can be an empty string.
* @param xfb_varyings An array of names of varyings to use for XFB. Can be NULL.
* @param n_xfb_varyings Amount of XFB varyings defined in @param xfb_varyings.Can be 0.
* @param out_vs_id Deref will be used to store GL id of a generated vertex shader.
* Can be NULL in which case no vertex shader will be used for the
* program object.
* @param out_tc_id Deref will be used to store GL id of a generated tess control shader.
* Can be NULL in which case no tess control shader will be used for the
* program object.
* @param out_te_id Deref will be used to store GL id of a generated tess evaluation shader.
* Can be NULL in which case no tess evaluation shader will be used for the
* program object.
* @param out_gs_id Deref will be used to store GL id of a generated geometry shader.
* Can be NULL in which case no geometry shader will be used for the
* program object.
* @param out_fs_id Deref will be used to store GL id of a generated fragment shader.
* Can be NULL in which case no fragment shader will be used for the
* program object.
* @param out_po_id Deref will be used to store GL id of a generated program object.
* Must not be NULL.
*
* @return true if the program was built successfully, false otherwise.
* */
bool Utils::buildProgram(const glw::Functions& gl, const std::string& vs_body, const std::string& tc_body,
const std::string& te_body, const std::string& gs_body, const std::string& fs_body,
const glw::GLchar** xfb_varyings, const unsigned int& n_xfb_varyings, glw::GLuint* out_vs_id,
glw::GLuint* out_tc_id, glw::GLuint* out_te_id, glw::GLuint* out_gs_id, glw::GLuint* out_fs_id,
glw::GLuint* out_po_id)
{
bool result = false;
/* Link the program object */
glw::GLint link_status = GL_FALSE;
/* Create objects, set up shader bodies and attach all requested shaders to the program object */
*out_po_id = gl.createProgram();
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() call failed.");
if (out_vs_id != DE_NULL)
{
const char* vs_body_raw_ptr = vs_body.c_str();
*out_vs_id = gl.createShader(GL_VERTEX_SHADER);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() call failed.");
gl.attachShader(*out_po_id, *out_vs_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader() call failed.");
gl.shaderSource(*out_vs_id, 1 /* count */, &vs_body_raw_ptr, DE_NULL /* length */);
GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() call failed.");
}
if (out_tc_id != DE_NULL)
{
const char* tc_body_raw_ptr = tc_body.c_str();
*out_tc_id = gl.createShader(GL_TESS_CONTROL_SHADER);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() call failed.");
gl.attachShader(*out_po_id, *out_tc_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader() call failed.");
gl.shaderSource(*out_tc_id, 1 /* count */, &tc_body_raw_ptr, DE_NULL /* length */);
GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() call failed.");
}
if (out_te_id != DE_NULL)
{
const char* te_body_raw_ptr = te_body.c_str();
*out_te_id = gl.createShader(GL_TESS_EVALUATION_SHADER);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() call failed.");
gl.attachShader(*out_po_id, *out_te_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader() call failed.");
gl.shaderSource(*out_te_id, 1 /* count */, &te_body_raw_ptr, DE_NULL /* length */);
GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() call failed.");
}
if (out_gs_id != DE_NULL)
{
const char* gs_body_raw_ptr = gs_body.c_str();
*out_gs_id = gl.createShader(GL_GEOMETRY_SHADER);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() call failed.");
gl.attachShader(*out_po_id, *out_gs_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader() call failed.");
gl.shaderSource(*out_gs_id, 1 /* count */, &gs_body_raw_ptr, DE_NULL /* length */);
GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() call failed.");
}
if (out_fs_id != DE_NULL)
{
const char* fs_body_raw_ptr = fs_body.c_str();
*out_fs_id = gl.createShader(GL_FRAGMENT_SHADER);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() call failed.");
gl.attachShader(*out_po_id, *out_fs_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader() call failed.");
gl.shaderSource(*out_fs_id, 1 /* count */, &fs_body_raw_ptr, DE_NULL /* length */);
GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() call failed.");
}
/* Compile all shaders */
const glw::GLuint so_ids[] = { (out_vs_id != DE_NULL) ? *out_vs_id : 0, (out_tc_id != DE_NULL) ? *out_tc_id : 0,
(out_te_id != DE_NULL) ? *out_te_id : 0, (out_gs_id != DE_NULL) ? *out_gs_id : 0,
(out_fs_id != DE_NULL) ? *out_fs_id : 0 };
const unsigned int n_so_ids = sizeof(so_ids) / sizeof(so_ids[0]);
for (unsigned int n_so_id = 0; n_so_id < n_so_ids; ++n_so_id)
{
glw::GLuint so_id = so_ids[n_so_id];
if (so_id != 0)
{
glw::GLint compile_status = GL_FALSE;
gl.compileShader(so_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader() call failed.");
gl.getShaderiv(so_id, GL_COMPILE_STATUS, &compile_status);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv() call failed.");
if (compile_status != GL_TRUE)
{
goto end;
}
} /* if (so_id != 0) */
} /* for (all shader objects) */
/* Set up XFB */
if (xfb_varyings != NULL)
{
gl.transformFeedbackVaryings(*out_po_id, n_xfb_varyings, xfb_varyings, GL_INTERLEAVED_ATTRIBS);
GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings() call failed.");
}
gl.linkProgram(*out_po_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glLinkProgram() call failed.");
gl.getProgramiv(*out_po_id, GL_LINK_STATUS, &link_status);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv() call failed.");
if (link_status != GL_TRUE)
{
goto end;
}
/* All done */
result = true;
end:
return result;
}
/** Retrieves base variable type for user-specified variable type
* (eg. float for vec4)
*
* @param variable_type Variable type to use for the query.
*
* @return As per description.
**/
Utils::_variable_type Utils::getBaseVariableType(const _variable_type& variable_type)
{
_variable_type result = VARIABLE_TYPE_UNKNOWN;
switch (variable_type)
{
case VARIABLE_TYPE_BOOL:
case VARIABLE_TYPE_BVEC2:
case VARIABLE_TYPE_BVEC3:
case VARIABLE_TYPE_BVEC4:
{
result = VARIABLE_TYPE_BOOL;
break;
}
case VARIABLE_TYPE_DOUBLE:
case VARIABLE_TYPE_DVEC2:
case VARIABLE_TYPE_DVEC3:
case VARIABLE_TYPE_DVEC4:
{
result = VARIABLE_TYPE_DOUBLE;
break;
}
case VARIABLE_TYPE_FLOAT:
case VARIABLE_TYPE_MAT2:
case VARIABLE_TYPE_MAT2X3:
case VARIABLE_TYPE_MAT2X4:
case VARIABLE_TYPE_MAT3:
case VARIABLE_TYPE_MAT3X2:
case VARIABLE_TYPE_MAT3X4:
case VARIABLE_TYPE_MAT4:
case VARIABLE_TYPE_MAT4X2:
case VARIABLE_TYPE_MAT4X3:
case VARIABLE_TYPE_VEC2:
case VARIABLE_TYPE_VEC3:
case VARIABLE_TYPE_VEC4:
{
result = VARIABLE_TYPE_FLOAT;
break;
}
case VARIABLE_TYPE_INT:
case VARIABLE_TYPE_IVEC2:
case VARIABLE_TYPE_IVEC3:
case VARIABLE_TYPE_IVEC4:
{
result = VARIABLE_TYPE_INT;
break;
}
case VARIABLE_TYPE_UINT:
case VARIABLE_TYPE_UVEC2:
case VARIABLE_TYPE_UVEC3:
case VARIABLE_TYPE_UVEC4:
{
result = VARIABLE_TYPE_UINT;
break;
}
default:
{
TCU_FAIL("Unrecognized variable type");
}
} /* switch (variable_type) */
return result;
}
/** Retrieves size of a single component (in bytes) for user-specified
* variable type.
*
* @param variable_type Variable type to use for the query.
*
* @return As per description.
**/
unsigned int Utils::getComponentSizeForVariableType(const _variable_type& variable_type)
{
_variable_type base_variable_type = getBaseVariableType(variable_type);
unsigned int result = 0;
switch (base_variable_type)
{
case VARIABLE_TYPE_BOOL:
result = sizeof(bool);
break;
case VARIABLE_TYPE_DOUBLE:
result = sizeof(double);
break;
case VARIABLE_TYPE_FLOAT:
result = sizeof(float);
break;
case VARIABLE_TYPE_INT:
result = sizeof(int);
break;
case VARIABLE_TYPE_UINT:
result = sizeof(unsigned int);
break;
default:
{
TCU_FAIL("Unrecognized base variable type");
}
} /* switch (variable_type) */
return result;
}
/** Retrieves a GLenum value corresponding to internal shader stage
* representation.
*
* @param shader_stage Shader stage to user for the query.
*
* @return Requested value or GL_NONE if the stage was not recognized.
**/
glw::GLenum Utils::getGLenumForShaderStage(const _shader_stage& shader_stage)
{
glw::GLenum result = GL_NONE;
switch (shader_stage)
{
case SHADER_STAGE_VERTEX:
result = GL_VERTEX_SHADER;
break;
case SHADER_STAGE_TESSELLATION_CONTROL:
result = GL_TESS_CONTROL_SHADER;
break;
case SHADER_STAGE_TESSELLATION_EVALUATION:
result = GL_TESS_EVALUATION_SHADER;
break;
case SHADER_STAGE_GEOMETRY:
result = GL_GEOMETRY_SHADER;
break;
case SHADER_STAGE_FRAGMENT:
result = GL_FRAGMENT_SHADER;
break;
default:
{
TCU_FAIL("Unrecognized shader stage requested");
}
} /* switch (shader_stage) */
return result;
}
/** Retrieves number of components that user-specified variable type supports.
*
* @param variable_type GLSL variable type to use for the query.
*
* @return As per description.
**/
unsigned int Utils::getNumberOfComponentsForVariableType(const _variable_type& variable_type)
{
unsigned int result = 0;
switch (variable_type)
{
case VARIABLE_TYPE_BOOL:
case VARIABLE_TYPE_DOUBLE:
case VARIABLE_TYPE_FLOAT:
case VARIABLE_TYPE_INT:
case VARIABLE_TYPE_UINT:
{
result = 1;
break;
}
case VARIABLE_TYPE_BVEC2:
case VARIABLE_TYPE_DVEC2:
case VARIABLE_TYPE_IVEC2:
case VARIABLE_TYPE_UVEC2:
case VARIABLE_TYPE_VEC2:
{
result = 2;
break;
}
case VARIABLE_TYPE_BVEC3:
case VARIABLE_TYPE_DVEC3:
case VARIABLE_TYPE_IVEC3:
case VARIABLE_TYPE_UVEC3:
case VARIABLE_TYPE_VEC3:
{
result = 3;
break;
}
case VARIABLE_TYPE_BVEC4:
case VARIABLE_TYPE_DVEC4:
case VARIABLE_TYPE_IVEC4:
case VARIABLE_TYPE_MAT2:
case VARIABLE_TYPE_UVEC4:
case VARIABLE_TYPE_VEC4:
{
result = 4;
break;
}
case VARIABLE_TYPE_MAT2X3:
case VARIABLE_TYPE_MAT3X2:
{
result = 6;
break;
}
case VARIABLE_TYPE_MAT2X4:
case VARIABLE_TYPE_MAT4X2:
{
result = 8;
break;
}
case VARIABLE_TYPE_MAT3:
{
result = 9;
break;
}
case VARIABLE_TYPE_MAT3X4:
case VARIABLE_TYPE_MAT4X3:
{
result = 12;
break;
}
case VARIABLE_TYPE_MAT4:
{
result = 16;
break;
}
default:
break;
} /* switch (variable_type) */
return result;
}
/** Retrieves a literal defining user-specified shader stage enum.
*
* @param shader_stage Shader stage to use for the query.
*
* @return Requested string or "?" if the stage was not recognized.
**/
std::string Utils::getShaderStageString(const _shader_stage& shader_stage)
{
std::string result = "?";
switch (shader_stage)
{
case SHADER_STAGE_FRAGMENT:
result = "Fragment Shader";
break;
case SHADER_STAGE_GEOMETRY:
result = "Geometry Shader";
break;
case SHADER_STAGE_TESSELLATION_CONTROL:
result = "Tessellation Control Shader";
break;
case SHADER_STAGE_TESSELLATION_EVALUATION:
result = "Tessellation Evaluation Shader";
break;
case SHADER_STAGE_VERTEX:
result = "Vertex Shader";
break;
default:
{
TCU_FAIL("Unrecognized shader stage");
}
} /* switch (shader_stage) */
return result;
}
/** Retrieves a literal defining user-specified shader stage enum.
*
* @param shader_stage_glenum Shader stage to use for the query.
*
* @return Requested string or "?" if the stage was not recognized.
**/
std::string Utils::getShaderStageStringFromGLEnum(const glw::GLenum shader_stage_glenum)
{
std::string result = "?";
switch (shader_stage_glenum)
{
case GL_FRAGMENT_SHADER:
result = "Fragment Shader";
break;
case GL_GEOMETRY_SHADER:
result = "Geometry Shader";
break;
case GL_TESS_CONTROL_SHADER:
result = "Tessellation Control Shader";
break;
case GL_TESS_EVALUATION_SHADER:
result = "Tessellation Evaluation Shader";
break;
case GL_VERTEX_SHADER:
result = "Vertex Shader";
break;
default:
{
TCU_FAIL("Unrecognized shader string");
}
} /* switch (shader_stage_glenum) */
return result;
}
/** Returns string that represents program interface name
*
* @param program_interface Program interface
*
* @return String representation of known program interface
**/
const GLchar* Utils::programInterfaceToStr(glw::GLenum program_interface)
{
const GLchar* string = "Unknown program interface";
switch (program_interface)
{
case GL_VERTEX_SUBROUTINE:
string = "GL_VERTEX_SUBROUTINE";
break;
case GL_VERTEX_SUBROUTINE_UNIFORM:
string = "GL_VERTEX_SUBROUTINE_UNIFORM";
break;
default:
TCU_FAIL("Not implemented");
}
return string;
}
/** Returns string that represents pname's name
*
* @param pname pname
*
* @return String representation of known pnames
**/
const GLchar* Utils::pnameToStr(glw::GLenum pname)
{
const GLchar* string = "Unknown pname";
switch (pname)
{
case GL_ACTIVE_SUBROUTINE_UNIFORMS:
string = "GL_ACTIVE_SUBROUTINE_UNIFORMS";
break;
case GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS:
string = "GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS";
break;
case GL_ACTIVE_SUBROUTINES:
string = "GL_ACTIVE_SUBROUTINES";
break;
case GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH:
string = "GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH";
break;
case GL_ACTIVE_SUBROUTINE_MAX_LENGTH:
string = "GL_ACTIVE_SUBROUTINE_MAX_LENGTH";
break;
case GL_NUM_COMPATIBLE_SUBROUTINES:
string = "GL_NUM_COMPATIBLE_SUBROUTINES";
break;
case GL_UNIFORM_SIZE:
string = "GL_UNIFORM_SIZE";
break;
case GL_COMPATIBLE_SUBROUTINES:
string = "GL_COMPATIBLE_SUBROUTINES";
break;
case GL_UNIFORM_NAME_LENGTH:
string = "GL_UNIFORM_NAME_LENGTH";
break;
case GL_ACTIVE_RESOURCES:
string = "GL_ACTIVE_RESOURCES";
break;
case GL_MAX_NAME_LENGTH:
string = "GL_MAX_NAME_LENGTH";
break;
case GL_MAX_NUM_COMPATIBLE_SUBROUTINES:
string = "GL_MAX_NUM_COMPATIBLE_SUBROUTINES";
break;
case GL_NAME_LENGTH:
string = "GL_NAME_LENGTH";
break;
case GL_ARRAY_SIZE:
string = "GL_ARRAY_SIZE";
break;
case GL_LOCATION:
string = "GL_LOCATION";
break;
default:
TCU_FAIL("Not implemented");
}
return string;
}
bool Utils::compare(const glw::GLfloat& left, const glw::GLfloat& right)
{
static const glw::GLfloat m_epsilon = 0.00001f;
if (m_epsilon < std::abs(right - left))
{
return false;
}
else
{
return true;
}
}
/** Returns a variable type enum corresponding to user-specified base variable type
* and the number of components it should support.
*
* @param base_variable_type Base variable type to use for the query.
* @param n_components Number of components to consider for the query.
*
* @return As per description.
**/
Utils::_variable_type Utils::getVariableTypeFromProperties(const _variable_type& base_variable_type,
const unsigned int& n_components)
{
_variable_type result = VARIABLE_TYPE_UNKNOWN;
switch (base_variable_type)
{
case VARIABLE_TYPE_BOOL:
{
switch (n_components)
{
case 1:
result = VARIABLE_TYPE_BOOL;
break;
case 2:
result = VARIABLE_TYPE_BVEC2;
break;
case 3:
result = VARIABLE_TYPE_BVEC3;
break;
case 4:
result = VARIABLE_TYPE_BVEC4;
break;
default:
{
TCU_FAIL("Unsupported number of components requested");
}
} /* switch (n_components) */
break;
}
case VARIABLE_TYPE_DOUBLE:
{
switch (n_components)
{
case 1:
result = VARIABLE_TYPE_DOUBLE;
break;
case 2:
result = VARIABLE_TYPE_DVEC2;
break;
case 3:
result = VARIABLE_TYPE_DVEC3;
break;
case 4:
result = VARIABLE_TYPE_DVEC4;
break;
default:
{
TCU_FAIL("Unsupported number of components requested");
}
} /* switch (n_components) */
break;
}
case VARIABLE_TYPE_FLOAT:
{
switch (n_components)
{
case 1:
result = VARIABLE_TYPE_FLOAT;
break;
case 2:
result = VARIABLE_TYPE_VEC2;
break;
case 3:
result = VARIABLE_TYPE_VEC3;
break;
case 4:
result = VARIABLE_TYPE_VEC4;
break;
default:
{
TCU_FAIL("Unsupported number of components requested");
}
} /* switch (n_components) */
break;
}
case VARIABLE_TYPE_INT:
{
switch (n_components)
{
case 1:
result = VARIABLE_TYPE_INT;
break;
case 2:
result = VARIABLE_TYPE_IVEC2;
break;
case 3:
result = VARIABLE_TYPE_IVEC3;
break;
case 4:
result = VARIABLE_TYPE_IVEC4;
break;
default:
{
TCU_FAIL("Unsupported number of components requested");
}
} /* switch (n_components) */
break;
}
case VARIABLE_TYPE_UINT:
{
switch (n_components)
{
case 1:
result = VARIABLE_TYPE_UINT;
break;
case 2:
result = VARIABLE_TYPE_UVEC2;
break;
case 3:
result = VARIABLE_TYPE_UVEC3;
break;
case 4:
result = VARIABLE_TYPE_UVEC4;
break;
default:
{
TCU_FAIL("Unsupported number of components requested");
}
} /* switch (n_components) */
break;
}
default:
{
TCU_FAIL("Unrecognized base variable type");
}
} /* switch (base_variable_type) */
return result;
}
/** Returns a GLSL literal corresponding to user-specified variable type.
*
* @param variable_type Variable type to use for the query.
*
* @return As per description or [?] if @param variable_type was not
* recognized.
**/
std::string Utils::getVariableTypeGLSLString(const _variable_type& variable_type)
{
std::string result = "[?]";
switch (variable_type)
{
case VARIABLE_TYPE_BOOL:
result = "bool";
break;
case VARIABLE_TYPE_BVEC2:
result = "bvec2";
break;
case VARIABLE_TYPE_BVEC3:
result = "bvec3";
break;
case VARIABLE_TYPE_BVEC4:
result = "bvec4";
break;
case VARIABLE_TYPE_DOUBLE:
result = "double";
break;
case VARIABLE_TYPE_DVEC2:
result = "dvec2";
break;
case VARIABLE_TYPE_DVEC3:
result = "dvec3";
break;
case VARIABLE_TYPE_DVEC4:
result = "dvec4";
break;
case VARIABLE_TYPE_FLOAT:
result = "float";
break;
case VARIABLE_TYPE_INT:
result = "int";
break;
case VARIABLE_TYPE_IVEC2:
result = "ivec2";
break;
case VARIABLE_TYPE_IVEC3:
result = "ivec3";
break;
case VARIABLE_TYPE_IVEC4:
result = "ivec4";
break;
case VARIABLE_TYPE_MAT2:
result = "mat2";
break;
case VARIABLE_TYPE_MAT2X3:
result = "mat2x3";
break;
case VARIABLE_TYPE_MAT2X4:
result = "mat2x4";
break;
case VARIABLE_TYPE_MAT3:
result = "mat3";
break;
case VARIABLE_TYPE_MAT3X2:
result = "mat3x2";
break;
case VARIABLE_TYPE_MAT3X4:
result = "mat3x4";
break;
case VARIABLE_TYPE_MAT4:
result = "mat4";
break;
case VARIABLE_TYPE_MAT4X2:
result = "mat4x2";
break;
case VARIABLE_TYPE_MAT4X3:
result = "mat4x3";
break;
case VARIABLE_TYPE_UINT:
result = "uint";
break;
case VARIABLE_TYPE_UVEC2:
result = "uvec2";
break;
case VARIABLE_TYPE_UVEC3:
result = "uvec3";
break;
case VARIABLE_TYPE_UVEC4:
result = "uvec4";
break;
case VARIABLE_TYPE_VEC2:
result = "vec2";
break;
case VARIABLE_TYPE_VEC3:
result = "vec3";
break;
case VARIABLE_TYPE_VEC4:
result = "vec4";
break;
default:
{
TCU_FAIL("Unrecognized variable type");
}
} /* switch (variable_type) */
return result;
}
/** Constructor.
*
* @param context Rendering context.
*
**/
APITest1::APITest1(deqp::Context& context)
: TestCase(context, "min_maxes", "Verifies the implementation returns valid GL_MAX_SUBROUTINE* pnames "
"which meet the minimum maximum requirements enforced by the spec.")
, m_has_test_passed(true)
{
/* Left blank intentionally */
}
/** Executes test iteration.
*
* @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed.
*/
tcu::TestNode::IterateResult APITest1::iterate()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Do not execute the test if GL_ARB_shader_subroutine is not supported */
if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_shader_subroutine"))
{
throw tcu::NotSupportedError("GL_ARB_shader_subroutine is not supported.");
}
/* Iterate over all pnames */
const struct
{
glw::GLenum pname;
const char* pname_string;
glw::GLint min_value;
} pnames[] = { { GL_MAX_SUBROUTINES, "GL_MAX_SUBROUTINES", 256 },
{ GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS, "GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS", 1024 } };
const unsigned int n_pnames = sizeof(pnames) / sizeof(pnames[0]);
for (unsigned int n_pname = 0; n_pname < n_pnames; ++n_pname)
{
glw::GLboolean bool_value = GL_FALSE;
glw::GLdouble double_value = 0.0;
glw::GLfloat float_value = 0.0f;
glw::GLint int_value = 0;
glw::GLint64 int64_value = 0;
const glw::GLint min_value = pnames[n_pname].min_value;
const glw::GLenum& pname = pnames[n_pname].pname;
const char* pname_string = pnames[n_pname].pname_string;
/* Retrieve the pname values */
gl.getBooleanv(pname, &bool_value);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetBooleanv() call failed.");
gl.getDoublev(pname, &double_value);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetDoublev() call failed.");
gl.getFloatv(pname, &float_value);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetFloatv() call failed.");
gl.getIntegerv(pname, &int_value);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() call failed.");
gl.getInteger64v(pname, &int64_value);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetInteger64v() call failed.");
/* Make sure the value reported meets the min max requirement */
if (int_value < min_value)
{
m_testCtx.getLog() << tcu::TestLog::Message << "GL implementation reports a value of [" << int_value
<< "]"
" for property ["
<< pname_string << "]"
", whereas the min max for the property is ["
<< min_value << "]." << tcu::TestLog::EndMessage;
m_has_test_passed = false;
}
/* Verify the other getters reported valid values */
const float epsilon = 1e-5f;
if (((int_value == 0) && (bool_value == GL_TRUE)) || ((int_value != 0) && (bool_value != GL_TRUE)))
{
m_testCtx.getLog() << tcu::TestLog::Message << "Invalid boolean value [" << bool_value
<< "]"
" reported for property ["
<< pname_string << "]"
" (int value:["
<< int_value << "])" << tcu::TestLog::EndMessage;
m_has_test_passed = false;
}
if (de::abs(double_value - (double)int_value) > epsilon)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Invalid double value [" << double_value
<< "]"
" reported for property ["
<< pname_string << "]"
" (int value:["
<< int_value << "])" << tcu::TestLog::EndMessage;
m_has_test_passed = false;
}
if (de::abs(float_value - (float)int_value) > epsilon)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Invalid float value [" << float_value
<< "]"
" reported for property ["
<< pname_string << "]"
" (int value:["
<< int_value << "])" << tcu::TestLog::EndMessage;
m_has_test_passed = false;
}
if (int64_value != int_value)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Invalid 64-bit integer value [" << float_value
<< "]"
" reported for property ["
<< pname_string << "]"
" (int value:["
<< int_value << "])" << tcu::TestLog::EndMessage;
m_has_test_passed = false;
}
} /* for (all pnames) */
if (m_has_test_passed)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
return STOP;
}
/** Constructor.
*
* @param context Rendering context.
*
**/
APITest2::APITest2(deqp::Context& context)
: TestCase(context, "name_getters", "Verifies glGetActiveSubroutineName() and glGetActiveSubroutineUniformName() "
"functions work correctly.")
, m_buffer(DE_NULL)
, m_has_test_passed(true)
, m_po_id(0)
, m_subroutine_name1("subroutine1")
, m_subroutine_name2("subroutine2")
, m_subroutine_uniform_name("data_provider")
, m_vs_id(0)
{
/* Left blank intentionally */
}
/** Destroys all ES objects that may have been created during test initialization,
* as well as releases any buffers that may have been allocated during the process.
*/
void APITest2::deinit()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
if (m_buffer != DE_NULL)
{
delete[] m_buffer;
m_buffer = DE_NULL;
}
if (m_po_id != 0)
{
gl.deleteProgram(m_po_id);
m_po_id = 0;
}
if (m_vs_id != 0)
{
gl.deleteShader(m_vs_id);
m_vs_id = 0;
}
}
/** Returns body of a vertex shader that should be used for the test.
*
* @return As per description.
**/
std::string APITest2::getVertexShaderBody()
{
return "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"subroutine int ExampleSubroutineType(int example_argument);\n"
"\n"
"subroutine(ExampleSubroutineType) int subroutine1(int example_argument)\n"
"{\n"
" return 1;\n"
"}\n"
"\n"
"subroutine(ExampleSubroutineType) int subroutine2(int example_argument)\n"
"{\n"
" return 2;\n"
"}\n"
"\n"
"subroutine uniform ExampleSubroutineType data_provider;\n"
"\n"
"void main()\n"
"{\n"
" gl_Position = vec4(float(data_provider(0)), vec3(1) );\n"
"}\n";
}
/** Initializes all ES objects required to run the test. */
void APITest2::initTest()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Generate program & shader objects */
m_po_id = gl.createProgram();
m_vs_id = gl.createShader(GL_VERTEX_SHADER);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() or glCreateShader() call(s) failed.");
/* Attach the shader to the program object */
gl.attachShader(m_po_id, m_vs_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader() call failed.");
/* Compile the shader */
glw::GLint compile_status = GL_FALSE;
std::string vs_body = getVertexShaderBody();
const char* vs_body_raw_ptr = vs_body.c_str();
gl.shaderSource(m_vs_id, 1 /* count */, &vs_body_raw_ptr, DE_NULL /* length */);
GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() call failed.");
gl.compileShader(m_vs_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader() call failed.");
gl.getShaderiv(m_vs_id, GL_COMPILE_STATUS, &compile_status);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv() call failed.");
if (compile_status != GL_TRUE)
{
TCU_FAIL("Shader compilation failed.");
}
/* Try to link the program object */
glw::GLint link_status = GL_FALSE;
gl.linkProgram(m_po_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glLinkProgram() call failed.");
gl.getProgramiv(m_po_id, GL_LINK_STATUS, &link_status);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv() call failed.");
if (link_status != GL_TRUE)
{
TCU_FAIL("Program linking failed.");
}
/* Perform a few sanity checks */
glw::GLint n_active_subroutines = 0;
glw::GLint n_active_subroutine_uniforms = 0;
gl.getProgramStageiv(m_po_id, GL_VERTEX_SHADER, GL_ACTIVE_SUBROUTINES, &n_active_subroutines);
gl.getProgramStageiv(m_po_id, GL_VERTEX_SHADER, GL_ACTIVE_SUBROUTINE_UNIFORMS, &n_active_subroutine_uniforms);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramStageiv() call failed.");
if (n_active_subroutines != 2 /* subroutines declared in vertex shader */)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Invalid amount of active subroutines reported; expected: 2,"
" reported:"
<< n_active_subroutines << tcu::TestLog::EndMessage;
TCU_FAIL("Invalid GL_ACTIVE_SUBROUTINES property value.");
}
if (n_active_subroutine_uniforms != 1)
{
m_testCtx.getLog() << tcu::TestLog::Message
<< "Invalid amount of active subroutine uniforms reported: expected: 1,"
" reported: "
<< n_active_subroutine_uniforms << tcu::TestLog::EndMessage;
TCU_FAIL("Invalid GL_ACTIVE_SUBROUTINE_UNIFORMS property value.");
}
}
/** Executes test iteration.
*
* @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed.
*/
tcu::TestNode::IterateResult APITest2::iterate()
{
/* Do not execute the test if GL_ARB_shader_subroutine is not supported */
if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_shader_subroutine"))
{
throw tcu::NotSupportedError("GL_ARB_shader_subroutine is not supported.");
}
/* Initialize a test program object */
initTest();
/* Verify glGetActiveSubroutineName() works correctly */
verifyGLGetActiveSubroutineNameFunctionality();
/* Verify glGetActiveSubroutineUniformName() works correctly */
verifyGLGetActiveSubroutineUniformNameFunctionality();
/* Done */
if (m_has_test_passed)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
return STOP;
}
/** Verifies glGetActiveSubroutineName() behaves as per GL_ARB_shader_subroutine
* specification.
**/
void APITest2::verifyGLGetActiveSubroutineNameFunctionality()
{
GLsizei expected_length1 = (GLsizei)strlen(m_subroutine_name1) + 1;
GLsizei expected_length2 = (GLsizei)strlen(m_subroutine_name1) + 1;
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
GLsizei reported_length = 0;
gl.getActiveSubroutineName(m_po_id, GL_VERTEX_SHADER, 0, /* index */
0, /* bufsize */
DE_NULL, /* length */
DE_NULL); /* name */
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetActiveSubroutineName() call failed.");
gl.getProgramInterfaceiv(m_po_id, GL_VERTEX_SUBROUTINE, GL_MAX_NAME_LENGTH, &reported_length);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetActiveSubroutineName() call failed.");
if ((reported_length != expected_length1) && (reported_length != expected_length2))
{
m_testCtx.getLog() << tcu::TestLog::Message
<< "Invalid active subroutine name length reported:" << reported_length
<< ", instead of: " << expected_length1 << " or " << expected_length2
<< tcu::TestLog::EndMessage;
TCU_FAIL("Incorrect length of active subroutine name");
}
m_buffer = new glw::GLchar[reported_length];
memset(m_buffer, 0, reported_length);
gl.getActiveSubroutineName(m_po_id, GL_VERTEX_SHADER, 0, reported_length, DE_NULL, /* length */
m_buffer);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetActiveSubroutineName() call failed.");
if (strcmp(m_buffer, m_subroutine_name1) != 0 && strcmp(m_buffer, m_subroutine_name2) != 0)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Invalid active subroutine name reported:[" << m_buffer
<< "]"
" instead of:["
<< m_subroutine_name1 << "]"
" or:["
<< m_subroutine_name2 << "]." << tcu::TestLog::EndMessage;
TCU_FAIL("Invalid active subroutine name reported.");
}
delete[] m_buffer;
m_buffer = DE_NULL;
}
/** Verifies glGetActiveSubroutineUniformName() behaves as per GL_ARB_shader_subroutine
* specification.
**/
void APITest2::verifyGLGetActiveSubroutineUniformNameFunctionality()
{
GLsizei expected_length = (GLsizei)strlen(m_subroutine_uniform_name);
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
GLsizei reported_length = 0;
gl.getActiveSubroutineUniformName(m_po_id, GL_VERTEX_SHADER, 0, /* index */
0, /* bufsize */
DE_NULL, /* length */
DE_NULL); /* name */
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetActiveSubroutineUniformName() call failed.");
gl.getActiveSubroutineUniformName(m_po_id, GL_VERTEX_SHADER, 0, /* index */
0, /* bufsize */
&reported_length, DE_NULL); /* name */
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetActiveSubroutineUniformName() call failed.");
// reported_length is the actual number of characters written into <name>
// If <bufSize> is 0, reported_length should be 0
if (reported_length != 0)
{
m_testCtx.getLog() << tcu::TestLog::Message
<< "Invalid active subroutine uniform name length reported:" << reported_length
<< ", instead of: " << 0 << tcu::TestLog::EndMessage;
TCU_FAIL("Incorrect length of active subroutine uniform name");
}
m_buffer = new glw::GLchar[expected_length + 1];
memset(m_buffer, 0, expected_length + 1);
gl.getActiveSubroutineUniformName(m_po_id, GL_VERTEX_SHADER, 0, expected_length + 1, &reported_length, m_buffer);
GLU_EXPECT_NO_ERROR(gl.getError(), "getActiveSubroutineUniformName() call failed.");
if (reported_length != expected_length)
{
m_testCtx.getLog() << tcu::TestLog::Message
<< "Invalid active subroutine uniform name length reported:" << reported_length
<< ", instead of: " << expected_length << tcu::TestLog::EndMessage;
TCU_FAIL("Incorrect length of active subroutine uniform name");
}
if (strcmp(m_buffer, m_subroutine_uniform_name) != 0)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Invalid active subroutine uniform name reported:[" << m_buffer
<< "]"
" instead of:["
<< m_subroutine_uniform_name << "]" << tcu::TestLog::EndMessage;
TCU_FAIL("Invalid active subroutine uniform name reported.");
}
delete[] m_buffer;
m_buffer = DE_NULL;
}
/** Constructor.
*
* @param context Rendering context.
*
**/
FunctionalTest1_2::FunctionalTest1_2(deqp::Context& context)
: TestCase(context, "two_subroutines_single_subroutine_uniform",
"Verifies the subroutines work correctly in a vertex shader for"
" bool/float/int/uint/double/*vec*/*mat* argument and return types")
, m_has_test_passed(true)
, m_po_id(0)
, m_po_getter0_subroutine_index(GL_INVALID_INDEX)
, m_po_getter1_subroutine_index(GL_INVALID_INDEX)
, m_po_subroutine_uniform_index(-1)
, m_xfb_bo_id(0)
, m_vao_id(0)
, m_vs_id(0)
{
/* Left blank intentionally */
}
/** Destroys all ES objects that may have been created during test initialization,
* as well as releases any buffers that may have been allocated during the process.
*/
void FunctionalTest1_2::deinit()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
deinitTestIteration();
if (m_xfb_bo_id != 0)
{
gl.deleteBuffers(1, &m_xfb_bo_id);
m_xfb_bo_id = 0;
}
if (m_vao_id != 0)
{
gl.deleteVertexArrays(1, &m_vao_id);
m_vao_id = 0;
}
}
/** Deinitializes GL objects that are iteration-specific */
void FunctionalTest1_2::deinitTestIteration()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
if (m_po_id != 0)
{
gl.deleteProgram(m_po_id);
m_po_id = 0;
}
if (m_vs_id != 0)
{
gl.deleteShader(m_vs_id);
m_vs_id = 0;
}
}
/** Executes a single test iteration using user-specified test case propertiesz.
*
* @param test-case Test case descriptor.
*
* @return true if the test iteration passed, false otherwise.
**/
bool FunctionalTest1_2::executeTestIteration(const _test_case& test_case)
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
bool result = true;
/* Build the test program */
std::string empty_body;
std::string vs_body = getVertexShaderBody(test_case.variable_type, test_case.array_size);
const glw::GLchar* xfb_varyings[] = { "result" };
const unsigned int n_xfb_varyings = sizeof(xfb_varyings) / sizeof(xfb_varyings[0]);
if (!Utils::buildProgram(gl, vs_body, empty_body, empty_body, empty_body, empty_body, xfb_varyings, n_xfb_varyings,
&m_vs_id, NULL, /* out_tc_id */
NULL, /* out_te_id */
NULL, /* out_gs_id */
NULL, &m_po_id))
{
TCU_FAIL("Test program failed to build.");
}
/* Retrieve subroutine locations */
m_po_getter0_subroutine_index = gl.getSubroutineIndex(m_po_id, GL_VERTEX_SHADER, "getter0");
m_po_getter1_subroutine_index = gl.getSubroutineIndex(m_po_id, GL_VERTEX_SHADER, "getter1");
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetSubroutineIndex() call(s) failed.");
if (m_po_getter0_subroutine_index == GL_INVALID_INDEX || m_po_getter1_subroutine_index == GL_INVALID_INDEX)
{
TCU_FAIL("At least one subroutine is considered inactive which is invalid.");
}
/* Retrieve subroutine uniform location */
m_po_subroutine_uniform_index = gl.getSubroutineUniformLocation(m_po_id, GL_VERTEX_SHADER, "colorGetterUniform");
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetSubroutineUniformLocation() call failed.");
if (m_po_subroutine_uniform_index == -1)
{
TCU_FAIL("Subroutine uniform is considered inactive which is invalid.");
}
/* Set up XFB BO storage */
const Utils::_variable_type base_variable_type = Utils::getBaseVariableType(test_case.variable_type);
unsigned int iteration_xfb_bo_size = Utils::getComponentSizeForVariableType(base_variable_type) *
Utils::getNumberOfComponentsForVariableType(test_case.variable_type);
unsigned int total_xfb_bo_size = 0;
if (base_variable_type == Utils::VARIABLE_TYPE_BOOL)
{
/* Boolean varyings are not supported by OpenGL. Instead, we use ints to output
* boolean values. */
iteration_xfb_bo_size = static_cast<unsigned int>(iteration_xfb_bo_size * sizeof(int));
}
total_xfb_bo_size = iteration_xfb_bo_size * 2 /* subroutines we will be testing */;
gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, total_xfb_bo_size, DE_NULL /* data */, GL_STATIC_DRAW);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() call failed.");
/* Activate test program object */
gl.useProgram(m_po_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() call failed.");
/* Run two iterations. Each iteration should invoke different subroutine. */
const glw::GLuint subroutine_indices[] = { m_po_getter0_subroutine_index, m_po_getter1_subroutine_index };
const unsigned int n_subroutine_indices = sizeof(subroutine_indices) / sizeof(subroutine_indices[0]);
for (unsigned int n_subroutine_index = 0; n_subroutine_index < n_subroutine_indices; ++n_subroutine_index)
{
/* Configure which subroutine should be used for the draw call */
glw::GLuint current_subroutine_index = subroutine_indices[n_subroutine_index];
gl.uniformSubroutinesuiv(GL_VERTEX_SHADER, 1 /* count */, &current_subroutine_index);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUniformSubroutinesuiv() call failed.");
/* Update XFB binding so that we do not overwrite data XFBed in previous iterations */
gl.bindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, /* index */
m_xfb_bo_id, iteration_xfb_bo_size * n_subroutine_index, iteration_xfb_bo_size);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferRange() call failed.");
/* Draw a single point */
gl.beginTransformFeedback(GL_POINTS);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback() call failed.");
{
gl.drawArrays(GL_POINTS, 0 /* first */, 1 /* count */);
GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays() call failed.");
}
gl.endTransformFeedback();
GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback() call failed.");
} /* for (all subroutine indices) */
/* Map the BO storage into process space */
const void* xfb_data_ptr = gl.mapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, GL_READ_ONLY);
GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBuffer() call failed.");
result &= verifyXFBData(xfb_data_ptr, test_case.variable_type);
gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffeR() call failed.");
return result;
}
/** Retrieves body of a vertex shader that should be used to verify
* subroutine support, given user-specified test iteration properties.
*
* @param variable_type GLSL type that should be used for argument and
* return type definition in a subroutine. This setting
* also affects type of the only output variable in the shader.
* @param array_size 1 if non-arrayed arguments/return types should be tested;
* 2 if arrayed arguments/return types should be tested.
*
* @return Requested string.
**/
std::string FunctionalTest1_2::getVertexShaderBody(const Utils::_variable_type& variable_type, unsigned int array_size)
{
Utils::_variable_type base_variable_type = Utils::getBaseVariableType(variable_type);
unsigned int n_variable_type_components = Utils::getNumberOfComponentsForVariableType(variable_type);
std::stringstream result_sstream;
std::string variable_type_glsl = Utils::getVariableTypeGLSLString(variable_type);
std::stringstream variable_type_glsl_array_sstream;
std::stringstream variable_type_glsl_arrayed_sstream;
variable_type_glsl_arrayed_sstream << variable_type_glsl;
if (array_size > 1)
{
variable_type_glsl_array_sstream << "[" << array_size << "]";
variable_type_glsl_arrayed_sstream << variable_type_glsl_array_sstream.str();
}
/* Form pre-amble */
result_sstream << "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n";
if (variable_type == Utils::VARIABLE_TYPE_DOUBLE)
{
result_sstream << "#extension GL_ARB_gpu_shader_fp64 : require\n";
}
/* Form subroutine type declaration */
result_sstream << "\n"
"subroutine "
<< variable_type_glsl_arrayed_sstream.str() << " colorGetter(in " << variable_type_glsl
<< " in_value" << variable_type_glsl_array_sstream.str() << ");\n"
"\n";
/* Declare getter functions */
for (int n_getter = 0; n_getter < 2; ++n_getter)
{
result_sstream << "subroutine(colorGetter) " << variable_type_glsl_arrayed_sstream.str() << " getter"
<< n_getter << "(in " << variable_type_glsl << " in_value"
<< variable_type_glsl_array_sstream.str() << ")\n"
"{\n";
if (array_size > 1)
{
result_sstream << variable_type_glsl << " temp" << variable_type_glsl_array_sstream.str() << ";\n";
}
if (base_variable_type == Utils::VARIABLE_TYPE_BOOL)
{
if (array_size > 1)
{
for (unsigned int array_index = 0; array_index < array_size; ++array_index)
{
result_sstream << " temp[" << array_index << "]"
" = "
<< ((n_getter == 0) ? ((variable_type_glsl == "bool") ? "!" : "not") : "")
<< "(in_value[" << array_index << "]);\n";
}
result_sstream << " return temp;\n";
}
else
{
result_sstream << " return "
<< ((n_getter == 0) ? ((variable_type_glsl == "bool") ? "!" : "not") : "")
<< "(in_value);\n";
}
} /* if (base_variable_type == Utils::VARIABLE_TYPE_BOOL) */
else
{
if (array_size > 1)
{
for (unsigned int array_index = 0; array_index < array_size; ++array_index)
{
result_sstream << " temp[" << array_index << "]"
" = in_value["
<< array_index << "] + " << (n_getter + 1) << ";\n";
}
result_sstream << " return temp;\n";
}
else
{
result_sstream << " return (in_value + " << (n_getter + 1) << ");\n";
}
}
result_sstream << "}\n";
} /* for (both getter functions) */
/* Declare subroutine uniform */
result_sstream << "subroutine uniform colorGetter colorGetterUniform;\n"
"\n";
/* Declare output variable */
result_sstream << "out ";
if (base_variable_type == Utils::VARIABLE_TYPE_BOOL)
{
Utils::_variable_type result_as_int_variable_type =
Utils::getVariableTypeFromProperties(Utils::VARIABLE_TYPE_INT, n_variable_type_components);
std::string variable_type_glsl_as_int = Utils::getVariableTypeGLSLString(result_as_int_variable_type);
result_sstream << variable_type_glsl_as_int;
}
else
{
result_sstream << variable_type_glsl;
}
result_sstream << " result;\n"
"\n";
/* Declare main(): prepare input argument for the subroutine function */
result_sstream << "void main()\n"
"{\n"
" "
<< variable_type_glsl << " temp";
if (array_size > 1)
{
result_sstream << "[" << array_size << "]";
}
result_sstream << ";\n";
for (unsigned int array_index = 0; array_index < array_size; ++array_index)
{
result_sstream << " temp";
if (array_size > 1)
{
result_sstream << "[" << array_index << "]";
}
result_sstream << " = " << variable_type_glsl << "(";
if (base_variable_type == Utils::VARIABLE_TYPE_BOOL)
{
result_sstream << "true";
}
else
{
for (unsigned int n_component = 0; n_component < n_variable_type_components; ++n_component)
{
result_sstream << "3";
if (n_component != (n_variable_type_components - 1))
{
result_sstream << ", ";
}
} /* for (all components) */
}
result_sstream << ");\n";
} /* for (all array indices) */
/* Declare main(): call the subroutine. Verify the input and write the result
* to the output variable.
**/
if (base_variable_type == Utils::VARIABLE_TYPE_BOOL)
{
Utils::_variable_type result_as_int_variable_type =
Utils::getVariableTypeFromProperties(Utils::VARIABLE_TYPE_INT, n_variable_type_components);
std::string variable_type_glsl_as_int = Utils::getVariableTypeGLSLString(result_as_int_variable_type);
result_sstream << variable_type_glsl_arrayed_sstream.str() << " subroutine_result = colorGetterUniform(temp);\n"
"result = ";
for (unsigned int array_index = 0; array_index < array_size; ++array_index)
{
if (variable_type_glsl == "bool")
result_sstream << "bool(subroutine_result";
else
result_sstream << "all(subroutine_result";
if (array_size > 1)
{
result_sstream << "[" << array_index << "]";
}
result_sstream << ")";
if (array_index != (array_size - 1))
{
result_sstream << "&& ";
}
}
result_sstream << " == true ? " << variable_type_glsl_as_int << "(1) : " << variable_type_glsl_as_int << "(0);";
}
else
{
if (array_size > 1)
{
DE_ASSERT(array_size == 2);
result_sstream << variable_type_glsl << " subroutine_result" << variable_type_glsl_array_sstream.str()
<< " = colorGetterUniform(temp);\n"
"\n"
"if (subroutine_result[0] == subroutine_result[1]) result = subroutine_result[0];\n"
"else\n"
"result = "
<< variable_type_glsl << "(-1);\n";
}
else
{
result_sstream << "result = colorGetterUniform(temp);\n";
}
}
/* All done */
result_sstream << "}\n";
return result_sstream.str();
}
/** Initializes all GL objects required to run the test. */
void FunctionalTest1_2::initTest()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Generate buffer object to hold result XFB data */
gl.genBuffers(1, &m_xfb_bo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers() call failed.");
/* Set up XFB BO bindings */
gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_xfb_bo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call failed.");
gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* index */, m_xfb_bo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase() call failed.");
/* Generate VAO to use for the draw calls */
gl.genVertexArrays(1, &m_vao_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenVertexArrays() call failed.");
gl.bindVertexArray(m_vao_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray() call failed.");
}
/** Executes test iteration.
*
* @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed.
*/
tcu::TestNode::IterateResult FunctionalTest1_2::iterate()
{
/* Do not execute the test if GL_ARB_shader_subroutine is not supported */
if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_shader_subroutine"))
{
throw tcu::NotSupportedError("GL_ARB_shader_subroutine is not supported.");
}
/* Initialize a test program object */
initTest();
/* Construct test case descriptors: first, iIerate over all
* variable types we want to cover */
const Utils::_variable_type variable_types[] = {
Utils::VARIABLE_TYPE_BOOL, Utils::VARIABLE_TYPE_BVEC2, Utils::VARIABLE_TYPE_BVEC3,
Utils::VARIABLE_TYPE_BVEC4, Utils::VARIABLE_TYPE_DOUBLE, Utils::VARIABLE_TYPE_FLOAT,
Utils::VARIABLE_TYPE_INT, Utils::VARIABLE_TYPE_IVEC2, Utils::VARIABLE_TYPE_IVEC3,
Utils::VARIABLE_TYPE_IVEC4, Utils::VARIABLE_TYPE_MAT2, Utils::VARIABLE_TYPE_MAT2X3,
Utils::VARIABLE_TYPE_MAT2X4, Utils::VARIABLE_TYPE_MAT3, Utils::VARIABLE_TYPE_MAT3X2,
Utils::VARIABLE_TYPE_MAT3X4, Utils::VARIABLE_TYPE_MAT4, Utils::VARIABLE_TYPE_MAT4X2,
Utils::VARIABLE_TYPE_MAT4X3, Utils::VARIABLE_TYPE_UINT, Utils::VARIABLE_TYPE_UVEC2,
Utils::VARIABLE_TYPE_UVEC3, Utils::VARIABLE_TYPE_UVEC4, Utils::VARIABLE_TYPE_VEC2,
Utils::VARIABLE_TYPE_VEC3, Utils::VARIABLE_TYPE_VEC4
};
const unsigned int n_variable_types = sizeof(variable_types) / sizeof(variable_types[0]);
for (unsigned int n_variable_type = 0; n_variable_type < n_variable_types; ++n_variable_type)
{
Utils::_variable_type current_variable_type = variable_types[n_variable_type];
/* We need to test both arrayed and non-arrayed arguments */
for (unsigned int array_size = 1; array_size < 3; ++array_size)
{
/* Exclude double variables if the relevant extension is unavailable */
if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_gpu_shader_fp64") &&
current_variable_type == Utils::VARIABLE_TYPE_DOUBLE)
{
continue;
}
/* Form the descriptor */
_test_case test_case;
test_case.array_size = array_size;
test_case.variable_type = current_variable_type;
/* Store the test case descriptor */
m_test_cases.push_back(test_case);
} /* for (both arrayed and non-arrayed arguments) */
} /* for (all variable types) */
/* Iterate over all test cases and execute the test */
for (_test_cases_const_iterator test_case_iterator = m_test_cases.begin(); test_case_iterator != m_test_cases.end();
++test_case_iterator)
{
const _test_case& test_case = *test_case_iterator;
m_has_test_passed &= executeTestIteration(test_case);
/* Release GL objects that were created during the execution */
deinitTestIteration();
} /* for (all test cases) */
/* Done */
if (m_has_test_passed)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
return STOP;
}
/** Verifies data that has been XFBed out by the vertex shader.
*
* @param xfb_data Buffer holding the data.
* @param variable_type GLSL type used for the test iteration
* that generated the data at @param xfb_data.
*
* @return true if the data was found to be valid, false if it
* was detected to be incorrect.
**/
bool FunctionalTest1_2::verifyXFBData(const void* xfb_data, const Utils::_variable_type& variable_type)
{
const Utils::_variable_type base_variable_type = Utils::getBaseVariableType(variable_type);
const float epsilon = 1e-5f;
const unsigned int n_variable_type_components = Utils::getNumberOfComponentsForVariableType(variable_type);
bool result = true;
const unsigned char* traveller_ptr = (const unsigned char*)xfb_data;
/* Boolean arguments/return types are tested with a slightly different shader so we
* need to test them in a separate code-path.
*/
if (base_variable_type == Utils::VARIABLE_TYPE_BOOL)
{
/* 0 should be returned when getter0 is used, 1 otherwise */
const unsigned int ref_values[] = { 0, 1 };
const unsigned int n_ref_values = sizeof(ref_values) / sizeof(ref_values[0]);
for (unsigned int n_ref_value = 0; n_ref_value < n_ref_values; ++n_ref_value)
{
const unsigned int ref_value = ref_values[n_ref_value];
for (unsigned int n_component = 0; n_component < n_variable_type_components; ++n_component)
{
int* result_value_ptr = (int*)(traveller_ptr);
if (*result_value_ptr != (int)ref_value)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Invalid value reported by subroutine using "
"["
<< Utils::getVariableTypeGLSLString(variable_type) << "]"
<< " argument/return types ("
"expected:["
<< ref_value << "], found:[" << *result_value_ptr << "])"
<< tcu::TestLog::EndMessage;
result = false;
break;
}
traveller_ptr += sizeof(int);
} /* for (all components) */
} /* for (all reference values) */
} /* if (base_variable_type == Utils::VARIABLE_TYPE_BOOL) */
else
{
/* 4 should be returned when getter0 is used, 5 otherwise */
const unsigned int ref_values[] = { 4, 5 };
const unsigned int n_ref_values = sizeof(ref_values) / sizeof(ref_values[0]);
for (unsigned int n_ref_value = 0; n_ref_value < n_ref_values; ++n_ref_value)
{
const unsigned int ref_value = ref_values[n_ref_value];
DE_ASSERT(
base_variable_type == Utils::VARIABLE_TYPE_DOUBLE || base_variable_type == Utils::VARIABLE_TYPE_FLOAT ||
base_variable_type == Utils::VARIABLE_TYPE_INT || base_variable_type == Utils::VARIABLE_TYPE_UINT);
for (unsigned int n_component = 0; n_component < n_variable_type_components; ++n_component)
{
const double* double_value_ptr = (double*)traveller_ptr;
const float* float_value_ptr = (float*)traveller_ptr;
const int* int_value_ptr = (int*)traveller_ptr;
switch (base_variable_type)
{
case Utils::VARIABLE_TYPE_DOUBLE:
{
if (de::abs(*double_value_ptr - (double)ref_value) > epsilon)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Invalid value reported by subroutine using "
"["
<< Utils::getVariableTypeGLSLString(variable_type) << "]"
<< " argument/return types ("
"expected:["
<< ref_value << "], found:[" << *double_value_ptr << "])"
<< tcu::TestLog::EndMessage;
result = false;
}
traveller_ptr += sizeof(double);
break;
}
case Utils::VARIABLE_TYPE_FLOAT:
{
if (de::abs(*float_value_ptr - (float)ref_value) > epsilon)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Invalid value reported by subroutine using "
"["
<< Utils::getVariableTypeGLSLString(variable_type) << "]"
<< " argument/return types ("
"expected:["
<< ref_value << "], found:[" << *float_value_ptr << "])"
<< tcu::TestLog::EndMessage;
result = false;
}
traveller_ptr += sizeof(float);
break;
}
case Utils::VARIABLE_TYPE_INT:
case Utils::VARIABLE_TYPE_UINT:
{
if (*int_value_ptr != (int)ref_value)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Invalid value reported by subroutine using "
"["
<< Utils::getVariableTypeGLSLString(variable_type) << "]"
<< " argument/return types ("
"expected:["
<< ref_value << "], found:[" << *int_value_ptr << "])"
<< tcu::TestLog::EndMessage;
result = false;
}
traveller_ptr += sizeof(int);
break;
}
default:
break;
} /* switch (base_variable_type) */
} /* for (all components) */
} /* for (all reference values) */
}
return result;
}
/** Constructor
*
* @param context CTS context
**/
FunctionalTest3_4::FunctionalTest3_4(deqp::Context& context)
: TestCase(context, "four_subroutines_with_two_uniforms", "Verify Get* API and draw calls")
, m_n_active_subroutine_uniforms(0)
, m_n_active_subroutine_uniform_locations(0)
, m_n_active_subroutines(0)
, m_n_active_subroutine_uniform_name_length(0)
, m_n_active_subroutine_name_length(0)
, m_n_active_subroutine_uniform_size(0)
{
/* Nothing to be done here */
}
/** Execute test
*
* @return tcu::TestNode::STOP
**/
tcu::TestNode::IterateResult FunctionalTest3_4::iterate()
{
static const glw::GLchar* vertex_shader_code =
"#version 400 core\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"precision highp float;\n"
"\n"
"// Sub routine type declaration\n"
"subroutine vec4 routine_type(in vec4 iparam);\n"
"\n"
"// Sub routine definitions\n"
"subroutine(routine_type) vec4 inverse_order(in vec4 iparam)\n"
"{\n"
" return iparam.wzyx;\n"
"}\n"
"\n"
"subroutine(routine_type) vec4 negate(in vec4 iparam)\n"
"{\n"
" return -iparam;\n"
"}\n"
"\n"
"subroutine(routine_type) vec4 inverse(in vec4 iparam)\n"
"{\n"
" return 1 / iparam;\n"
"}\n"
"\n"
"subroutine(routine_type) vec4 square(in vec4 iparam)\n"
"{\n"
" return iparam * iparam;\n"
"}\n"
"\n"
"// Sub routine uniforms\n"
"subroutine uniform routine_type first_routine;\n"
"subroutine uniform routine_type second_routine;\n"
"\n"
"// Input data\n"
"uniform vec4 input_data;\n"
"\n"
"// Output\n"
"out vec4 out_input_data;\n"
"out vec4 out_result_from_first_routine;\n"
"out vec4 out_result_from_second_routine;\n"
"out vec4 out_result_from_combined_routines;\n"
"out vec4 out_result_from_routines_combined_in_reveresed_order;\n"
"\n"
"void main()\n"
"{\n"
" out_input_data = input_data;\n"
" out_result_from_first_routine = first_routine(input_data);\n"
" out_result_from_second_routine = second_routine(input_data);\n"
" out_result_from_combined_routines = second_routine(first_routine(input_data));\n"
" out_result_from_routines_combined_in_reveresed_order = first_routine(second_routine(input_data));\n"
"}\n"
"\n";
static const GLchar* varying_names[] = {
"out_input_data",
"out_result_from_first_routine",
"out_result_from_second_routine",
"out_result_from_combined_routines",
"out_result_from_routines_combined_in_reveresed_order",
};
static const GLchar* subroutine_uniform_names[] = { "first_routine", "second_routine" };
static const GLchar* subroutine_names[] = { "inverse_order", "negate", "inverse", "square" };
static const GLuint n_varyings = sizeof(varying_names) / sizeof(varying_names[0]);
static const GLuint transform_feedback_buffer_size = n_varyings * sizeof(GLfloat) * 4 /* vec4 */;
static const GLuint inverse_order_routine_index = 0;
static const GLuint negate_routine_index = 1;
static const GLuint inverse_routine_index = 2;
static const GLuint square_routine_index = 3;
/* Test data */
static const Utils::vec4<GLfloat> inverse_order_negate_data[5] = {
Utils::vec4<GLfloat>(-2.0f, -1.0f, 1.0f, 2.0f), Utils::vec4<GLfloat>(2.0f, 1.0f, -1.0f, -2.0f),
Utils::vec4<GLfloat>(2.0f, 1.0f, -1.0f, -2.0f), Utils::vec4<GLfloat>(-2.0f, -1.0f, 1.0f, 2.0f),
Utils::vec4<GLfloat>(-2.0f, -1.0f, 1.0f, 2.0f),
};
static const Utils::vec4<GLfloat> inverse_order_inverse_data[5] = {
Utils::vec4<GLfloat>(-2.0f, -1.0f, 1.0f, 2.0f), Utils::vec4<GLfloat>(2.0f, 1.0f, -1.0f, -2.0f),
Utils::vec4<GLfloat>(-0.5f, -1.0f, 1.0f, 0.5f), Utils::vec4<GLfloat>(0.5f, 1.0f, -1.0f, -0.5f),
Utils::vec4<GLfloat>(0.5f, 1.0f, -1.0f, -0.5f),
};
static const Utils::vec4<GLfloat> inverse_order_square_data[5] = {
Utils::vec4<GLfloat>(-2.0f, -1.0f, 1.0f, 2.0f), Utils::vec4<GLfloat>(2.0f, 1.0f, -1.0f, -2.0f),
Utils::vec4<GLfloat>(4.0f, 1.0f, 1.0f, 4.0f), Utils::vec4<GLfloat>(4.0f, 1.0f, 1.0f, 4.0f),
Utils::vec4<GLfloat>(4.0f, 1.0f, 1.0f, 4.0f),
};
static const Utils::vec4<GLfloat> negate_inverse_data[5] = {
Utils::vec4<GLfloat>(-2.0f, -1.0f, 1.0f, 2.0f), Utils::vec4<GLfloat>(2.0f, 1.0f, -1.0f, -2.0f),
Utils::vec4<GLfloat>(-0.5f, -1.0f, 1.0f, 0.5f), Utils::vec4<GLfloat>(0.5f, 1.0f, -1.0f, -0.5f),
Utils::vec4<GLfloat>(0.5f, 1.0f, -1.0f, -0.5f),
};
static const Utils::vec4<GLfloat> negate_square_data[5] = {
Utils::vec4<GLfloat>(-2.0f, -1.0f, 1.0f, 2.0f), Utils::vec4<GLfloat>(2.0f, 1.0f, -1.0f, -2.0f),
Utils::vec4<GLfloat>(4.0f, 1.0f, 1.0f, 4.0f), Utils::vec4<GLfloat>(4.0f, 1.0f, 1.0f, 4.0f),
Utils::vec4<GLfloat>(-4.0f, -1.0f, -1.0f, -4.0f),
};
static const Utils::vec4<GLfloat> inverse_square_data[5] = {
Utils::vec4<GLfloat>(-2.0f, -1.0f, 1.0f, 2.0f), Utils::vec4<GLfloat>(-0.5f, -1.0f, 1.0f, 0.5f),
Utils::vec4<GLfloat>(4.0f, 1.0f, 1.0f, 4.0f), Utils::vec4<GLfloat>(0.25f, 1.0f, 1.0f, 0.25f),
Utils::vec4<GLfloat>(0.25f, 1.0f, 1.0f, 0.25f),
};
/* Do not execute the test if GL_ARB_shader_subroutine is not supported */
if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_shader_subroutine"))
{
throw tcu::NotSupportedError("GL_ARB_shader_subroutine is not supported.");
}
m_n_active_subroutine_uniforms = 2;
m_n_active_subroutine_uniform_locations = 2;
m_n_active_subroutines = 4;
m_n_active_subroutine_uniform_name_length = 0;
m_n_active_subroutine_name_length = 0;
m_n_active_subroutine_uniform_size = 1;
/* GL objects */
Utils::program program(m_context);
Utils::buffer transform_feedback_buffer(m_context);
Utils::vertexArray vao(m_context);
bool result = true;
/* Calculate max name lengths for subroutines and subroutine uniforms */
for (GLint i = 0; i < m_n_active_subroutine_uniforms; ++i)
{
const GLsizei length = (GLsizei)strlen(subroutine_uniform_names[i]);
if (length > m_n_active_subroutine_uniform_name_length)
{
m_n_active_subroutine_uniform_name_length = length;
}
}
for (GLint i = 0; i < m_n_active_subroutines; ++i)
{
const GLsizei length = (GLsizei)strlen(subroutine_names[i]);
if (length > m_n_active_subroutine_name_length)
{
m_n_active_subroutine_name_length = length;
}
}
/* Init */
program.build(0 /* cs */, 0 /* fs */, 0 /* gs */, 0 /* tcs */, 0 /* test */, vertex_shader_code, varying_names,
n_varyings);
vao.generate();
vao.bind();
transform_feedback_buffer.generate();
transform_feedback_buffer.update(GL_TRANSFORM_FEEDBACK_BUFFER, transform_feedback_buffer_size, 0 /* data */,
GL_DYNAMIC_COPY);
transform_feedback_buffer.bindRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0, transform_feedback_buffer_size);
program.use();
/* Inspect Get* API */
if ((false == inspectProgramStageiv(program.m_program_object_id)) ||
(false == inspectActiveSubroutineUniformiv(program.m_program_object_id, subroutine_uniform_names)) ||
(false == inspectActiveSubroutineUniformName(program.m_program_object_id, subroutine_uniform_names)) ||
(false == inspectActiveSubroutineName(program.m_program_object_id, subroutine_names)) ||
(false ==
inspectSubroutineBinding(program.m_program_object_id, subroutine_names, subroutine_uniform_names, false)))
{
result = false;
}
/* Inspect GetProgram* API */
if (true == m_context.getContextInfo().isExtensionSupported("GL_ARB_program_interface_query"))
{
if ((false == inspectProgramInterfaceiv(program.m_program_object_id)) ||
(false ==
inspectProgramResourceiv(program.m_program_object_id, subroutine_names, subroutine_uniform_names)) ||
(false ==
inspectSubroutineBinding(program.m_program_object_id, subroutine_names, subroutine_uniform_names, true)))
{
result = false;
}
}
/* Test shader execution */
if ((false == testDraw(program.m_program_object_id, subroutine_names[inverse_order_routine_index],
subroutine_names[negate_routine_index], subroutine_uniform_names, inverse_order_negate_data,
false)) ||
(false == testDraw(program.m_program_object_id, subroutine_names[inverse_order_routine_index],
subroutine_names[inverse_routine_index], subroutine_uniform_names,
inverse_order_inverse_data, false)) ||
(false == testDraw(program.m_program_object_id, subroutine_names[inverse_order_routine_index],
subroutine_names[square_routine_index], subroutine_uniform_names, inverse_order_square_data,
false)) ||
(false == testDraw(program.m_program_object_id, subroutine_names[negate_routine_index],
subroutine_names[inverse_routine_index], subroutine_uniform_names, negate_inverse_data,
false)) ||
(false == testDraw(program.m_program_object_id, subroutine_names[negate_routine_index],
subroutine_names[square_routine_index], subroutine_uniform_names, negate_square_data,
false)) ||
(false == testDraw(program.m_program_object_id, subroutine_names[inverse_routine_index],
subroutine_names[square_routine_index], subroutine_uniform_names, inverse_square_data,
false)))
{
result = false;
}
if (true == m_context.getContextInfo().isExtensionSupported("GL_ARB_program_interface_query"))
{
if ((false == testDraw(program.m_program_object_id, subroutine_names[inverse_order_routine_index],
subroutine_names[negate_routine_index], subroutine_uniform_names,
inverse_order_negate_data, true)) ||
(false == testDraw(program.m_program_object_id, subroutine_names[inverse_order_routine_index],
subroutine_names[inverse_routine_index], subroutine_uniform_names,
inverse_order_inverse_data, true)) ||
(false == testDraw(program.m_program_object_id, subroutine_names[inverse_order_routine_index],
subroutine_names[square_routine_index], subroutine_uniform_names,
inverse_order_square_data, true)) ||
(false == testDraw(program.m_program_object_id, subroutine_names[negate_routine_index],
subroutine_names[inverse_routine_index], subroutine_uniform_names, negate_inverse_data,
true)) ||
(false == testDraw(program.m_program_object_id, subroutine_names[negate_routine_index],
subroutine_names[square_routine_index], subroutine_uniform_names, negate_square_data,
true)) ||
(false == testDraw(program.m_program_object_id, subroutine_names[inverse_routine_index],
subroutine_names[square_routine_index], subroutine_uniform_names, inverse_square_data,
true)))
{
result = false;
}
}
/* Done */
if (true == result)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
return tcu::TestNode::STOP;
}
/** Verify result of getProgramStageiv
*
* @param program_id Program object id
* @param pname <pname> parameter for getProgramStageiv
* @param expected Expected value
*
* @return true if result is equal to expected value, flase otherwise
**/
bool FunctionalTest3_4::checkProgramStageiv(glw::GLuint program_id, glw::GLenum pname, glw::GLint expected) const
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
GLint value = 0;
gl.getProgramStageiv(program_id, GL_VERTEX_SHADER, pname, &value);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramStageiv");
if (expected != value)
{
m_context.getTestContext().getLog() << tcu::TestLog::Message
<< "Error. Invalid result. Function: getProgramStageiv. "
<< "pname: " << Utils::pnameToStr(pname) << ". "
<< "Result: " << value << ". "
<< "Expected: " << expected << "." << tcu::TestLog::EndMessage;
return false;
}
else
{
return true;
}
}
/** Verify result of getProgramResourceiv
*
* @param program_id Program object id
* @param program_interface Program interface
* @param pname <pname> parameter for getProgramStageiv
* @param resource_name Resource name
* @param expected Expected value
*
* @return true if result is equal to expected value, false otherwise
**/
bool FunctionalTest3_4::checkProgramResourceiv(GLuint program_id, GLenum program_interface, GLenum pname,
const glw::GLchar* resource_name, GLint expected) const
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
GLuint index = gl.getProgramResourceIndex(program_id, program_interface, resource_name);
GLint value = 0;
if (GL_INVALID_INDEX == index)
{
return false;
}
gl.getProgramResourceiv(program_id, program_interface, index, 1, &pname, 1, 0, &value);
GLU_EXPECT_NO_ERROR(gl.getError(), "getProgramResourceiv");
if (expected != value)
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "Error. Invalid result. Function: getProgramResourceiv. "
<< "Program interface: " << Utils::programInterfaceToStr(program_interface) << ". "
<< "Resource name: " << resource_name << ". "
<< "Property: " << Utils::pnameToStr(pname) << ". "
<< "Result: " << value << ". "
<< "Expected: " << expected << "." << tcu::TestLog::EndMessage;
return false;
}
else
{
return true;
}
}
/** Verify result of getProgramInterfaceiv
*
* @param program_id Program object id
* @param program_interface Program interface
* @param pname <pname> parameter for getProgramStageiv
* @param expected Expected value
*
* @return true if result is equal to expected value, flase otherwise
**/
bool FunctionalTest3_4::checkProgramInterfaceiv(GLuint program_id, GLenum program_interface, GLenum pname,
GLint expected) const
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
GLint value = 0;
gl.getProgramInterfaceiv(program_id, program_interface, pname, &value);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramInterfaceiv");
if (expected != value)
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "Error. Invalid result. Function: getProgramInterfaceiv. "
<< "Program interface: " << Utils::programInterfaceToStr(program_interface) << ". "
<< "pname: " << Utils::pnameToStr(pname) << ". "
<< "Result: " << value << ". "
<< "Expected: " << expected << "." << tcu::TestLog::EndMessage;
return false;
}
else
{
return true;
}
}
/** Verify result of getActiveSubroutineUniformiv
*
* @param program_id Program object id
* @param index <index> parameter for getActiveSubroutineUniformiv
* @param pname <pname> parameter for getActiveSubroutineUniformiv
* @param expected Expected value
*
* @return true if result is equal to expected value, flase otherwise
**/
bool FunctionalTest3_4::checkActiveSubroutineUniformiv(GLuint program_id, GLuint index, GLenum pname,
GLint expected) const
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
GLint value = 0;
gl.getActiveSubroutineUniformiv(program_id, GL_VERTEX_SHADER, index, pname, &value);
GLU_EXPECT_NO_ERROR(gl.getError(), "getActiveSubroutineUniformiv");
if (expected != value)
{
m_context.getTestContext().getLog() << tcu::TestLog::Message
<< "Error. Invalid result. Function: getActiveSubroutineUniformiv. "
<< "idnex: " << index << ". "
<< "pname: " << Utils::pnameToStr(pname) << ". "
<< "Result: " << value << ". "
<< "Expected: " << expected << "." << tcu::TestLog::EndMessage;
return false;
}
else
{
return true;
}
}
/** Returns index of program resource
*
* @param program_id Program object id
* @param program_interface Program interface
* @param resource_name Name of resource
*
* @return Index of specified resource
**/
GLuint FunctionalTest3_4::getProgramResourceIndex(GLuint program_id, GLenum program_interface,
const glw::GLchar* resource_name) const
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
GLuint index = gl.getProgramResourceIndex(program_id, program_interface, resource_name);
GLU_EXPECT_NO_ERROR(gl.getError(), "getProgramResourceIndex");
if (GL_INVALID_INDEX == index)
{
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Error. Program resource is not available. "
<< "Program interface: " << Utils::programInterfaceToStr(program_interface)
<< ". "
<< "Resource name: " << resource_name << "." << tcu::TestLog::EndMessage;
}
return index;
}
/** Get subroutine index
*
* @param program_id Program object id
* @param subroutine_name Subroutine name
* @param use_program_query If true getProgramResourceIndex is used, otherwise getSubroutineIndex
*
* @return Index of subroutine
**/
GLuint FunctionalTest3_4::getSubroutineIndex(GLuint program_id, const glw::GLchar* subroutine_name,
bool use_program_query) const
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
GLuint index = -1;
if (false == use_program_query)
{
index = gl.getSubroutineIndex(program_id, GL_VERTEX_SHADER, subroutine_name);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetSubroutineIndex");
}
else
{
index = gl.getProgramResourceIndex(program_id, GL_VERTEX_SUBROUTINE, subroutine_name);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramResourceIndex");
}
if (GL_INVALID_INDEX == index)
{
TCU_FAIL("Subroutine is not available");
}
return index;
}
/** Get subroutine uniform location
*
* @param program_id Program object id
* @param uniform_name Subroutine uniform name
* @param use_program_query If true getProgramResourceLocation is used, otherwise getSubroutineUniformLocation
*
* @return Location of subroutine uniform
**/
GLint FunctionalTest3_4::getSubroutineUniformLocation(GLuint program_id, const glw::GLchar* uniform_name,
bool use_program_query) const
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
GLint location = -1;
if (false == use_program_query)
{
location = gl.getSubroutineUniformLocation(program_id, GL_VERTEX_SHADER, uniform_name);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetSubroutineUniformLocation");
}
else
{
location = gl.getProgramResourceLocation(program_id, GL_VERTEX_SUBROUTINE_UNIFORM, uniform_name);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramResourceLocation");
}
if (-1 == location)
{
TCU_FAIL("Subroutine uniform is not available");
}
return location;
}
/** Test if getProgramStageiv results are as expected
*
* @param program_id Program object id
*
* @result false in case of invalid result for any pname, true otherwise
**/
bool FunctionalTest3_4::inspectProgramStageiv(glw::GLuint program_id) const
{
bool result = true;
const inspectionDetails details[] = {
{ GL_ACTIVE_SUBROUTINE_UNIFORMS, m_n_active_subroutine_uniforms },
{ GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS, m_n_active_subroutine_uniform_locations },
{ GL_ACTIVE_SUBROUTINES, m_n_active_subroutines },
{ GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH, m_n_active_subroutine_uniform_name_length + 1 },
{ GL_ACTIVE_SUBROUTINE_MAX_LENGTH, m_n_active_subroutine_name_length + 1 }
};
const GLuint n_details = sizeof(details) / sizeof(details[0]);
for (GLuint i = 0; i < n_details; ++i)
{
if (false == checkProgramStageiv(program_id, details[i].pname, details[i].expected_value))
{
result = false;
}
}
return result;
}
/** Test if checkProgramInterfaceiv results are as expected
*
* @param program_id Program object id
*
* @result false in case of invalid result for any pname, true otherwise
**/
bool FunctionalTest3_4::inspectProgramInterfaceiv(glw::GLuint program_id) const
{
bool result = true;
const inspectionDetailsForProgramInterface details[] = {
{ GL_VERTEX_SUBROUTINE_UNIFORM, GL_ACTIVE_RESOURCES, m_n_active_subroutine_uniforms },
{ GL_VERTEX_SUBROUTINE_UNIFORM, GL_MAX_NAME_LENGTH, m_n_active_subroutine_uniform_name_length + 1 },
{ GL_VERTEX_SUBROUTINE_UNIFORM, GL_MAX_NUM_COMPATIBLE_SUBROUTINES, m_n_active_subroutines },
{ GL_VERTEX_SUBROUTINE, GL_ACTIVE_RESOURCES, m_n_active_subroutines },
{ GL_VERTEX_SUBROUTINE, GL_MAX_NAME_LENGTH, m_n_active_subroutine_name_length + 1 }
};
const GLuint n_details = sizeof(details) / sizeof(details[0]);
for (GLuint i = 0; i < n_details; ++i)
{
if (false == checkProgramInterfaceiv(program_id, details[i].program_interface, details[i].pname,
details[i].expected_value))
{
result = false;
}
}
return result;
}
/** Test if checkProgramResourceiv results are as expected
*
* @param program_id Program object id
* @param subroutine_names Array of subroutine names
* @param uniform_names Array of uniform names
*
* @result false in case of invalid result for any pname, true otherwise
**/
bool FunctionalTest3_4::inspectProgramResourceiv(GLuint program_id, const GLchar** subroutine_names,
const GLchar** uniform_names) const
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
bool result = true;
for (GLint subroutine = 0; subroutine < m_n_active_subroutines; ++subroutine)
{
const GLchar* subroutine_name = subroutine_names[subroutine];
const GLint length = (GLint)strlen(subroutine_name) + 1;
if (false == checkProgramResourceiv(program_id, GL_VERTEX_SUBROUTINE, GL_NAME_LENGTH, subroutine_name, length))
{
result = false;
}
}
inspectionDetails details[] = {
{ GL_NAME_LENGTH, 0 },
{ GL_ARRAY_SIZE, 1 },
{ GL_NUM_COMPATIBLE_SUBROUTINES, m_n_active_subroutines },
{ GL_LOCATION, 0 },
};
const GLuint n_details = sizeof(details) / sizeof(details[0]);
for (GLint uniform = 0; uniform < m_n_active_subroutine_uniforms; ++uniform)
{
const GLchar* uniform_name = uniform_names[uniform];
const GLint length = (GLint)strlen(uniform_name) + 1;
const GLint location = getSubroutineUniformLocation(program_id, uniform_name, true);
details[0].expected_value = length;
details[3].expected_value = location;
for (GLuint i = 0; i < n_details; ++i)
{
if (false == checkProgramResourceiv(program_id, GL_VERTEX_SUBROUTINE_UNIFORM, details[i].pname,
uniform_name, details[i].expected_value))
{
result = false;
}
}
/* Check compatible subroutines */
GLuint index = getProgramResourceIndex(program_id, GL_VERTEX_SUBROUTINE_UNIFORM, uniform_name);
if (GL_INVALID_INDEX != index)
{
std::vector<GLint> compatible_subroutines;
GLint index_sum = 0;
GLenum prop = GL_COMPATIBLE_SUBROUTINES;
compatible_subroutines.resize(m_n_active_subroutines);
gl.getProgramResourceiv(program_id, GL_VERTEX_SUBROUTINE_UNIFORM, index, 1, &prop, m_n_active_subroutines,
0, &compatible_subroutines[0]);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramResourceiv");
/* Expected indices are 0, 1, 2, ... N */
for (GLint i = 0; i < m_n_active_subroutines; ++i)
{
index_sum += compatible_subroutines[i];
}
/* Sum of E1, ..., EN = (E1 + EN) * N / 2 */
if (((m_n_active_subroutines - 1) * m_n_active_subroutines) / 2 != index_sum)
{
tcu::MessageBuilder message = m_context.getTestContext().getLog() << tcu::TestLog::Message;
message << "Error. Invalid result. Function: getProgramResourceiv. "
<< "Program interface: GL_VERTEX_SUBROUTINE_UNIFORM. "
<< "Resource name: " << uniform_name << ". "
<< "Property: GL_COMPATIBLE_SUBROUTINES. "
<< "Results: ";
for (GLint i = 1; i < m_n_active_subroutines; ++i)
{
message << compatible_subroutines[i];
}
message << tcu::TestLog::EndMessage;
result = false;
}
}
}
return result;
}
/** Test if getActiveSubroutineUniformiv results are as expected
*
* @param program_id Program object id
* @param uniform_names Array of subroutine uniform names available in program
*
* @result false in case of invalid result for any pname, true otherwise
**/
bool FunctionalTest3_4::inspectActiveSubroutineUniformiv(GLuint program_id, const GLchar** uniform_names) const
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
bool result = true;
GLint n_active_subroutine_uniforms = 0;
inspectionDetails details[] = {
{ GL_NUM_COMPATIBLE_SUBROUTINES, m_n_active_subroutines },
{ GL_UNIFORM_SIZE, m_n_active_subroutine_uniform_size },
{ GL_UNIFORM_NAME_LENGTH, 0 },
};
const GLuint n_details = sizeof(details) / sizeof(details[0]);
/* Get amount of active subroutine uniforms */
gl.getProgramStageiv(program_id, GL_VERTEX_SHADER, GL_ACTIVE_SUBROUTINE_UNIFORMS, &n_active_subroutine_uniforms);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramStageiv");
for (GLint uniform = 0; uniform < n_active_subroutine_uniforms; ++uniform)
{
GLint name_length = (GLint)strlen(uniform_names[uniform]);
details[2].expected_value = name_length + 1;
/* Checks from "details" */
for (GLuint i = 0; i < n_details; ++i)
{
if (false ==
checkActiveSubroutineUniformiv(program_id, uniform, details[i].pname, details[i].expected_value))
{
result = false;
}
}
/* Check compatible subroutines */
std::vector<GLint> compatible_subroutines;
compatible_subroutines.resize(m_n_active_subroutines);
GLint index_sum = 0;
gl.getActiveSubroutineUniformiv(program_id, GL_VERTEX_SHADER, uniform, GL_COMPATIBLE_SUBROUTINES,
&compatible_subroutines[0]);
GLU_EXPECT_NO_ERROR(gl.getError(), "getActiveSubroutineUniformiv");
/* Expected indices are 0, 1, 2, ... N */
for (GLint i = 0; i < m_n_active_subroutines; ++i)
{
index_sum += compatible_subroutines[i];
}
/* Sum of E1, ..., EN = (E1 + EN) * N / 2 */
if (((m_n_active_subroutines - 1) * m_n_active_subroutines) / 2 != index_sum)
{
tcu::MessageBuilder message = m_context.getTestContext().getLog() << tcu::TestLog::Message;
message << "Error. Invalid result. Function: getActiveSubroutineUniformiv. idnex: " << uniform
<< ". pname: " << Utils::pnameToStr(GL_COMPATIBLE_SUBROUTINES) << ". Results: ";
for (GLint i = 1; i < m_n_active_subroutines; ++i)
{
message << compatible_subroutines[i];
}
message << tcu::TestLog::EndMessage;
result = false;
}
}
return result;
}
/** Test if getActiveSubroutineUniformName results are as expected
*
* @param program_id Program object id
* @param uniform_names Array of subroutine uniform names available in program
*
* @result false in case of invalid result, true otherwise
**/
bool FunctionalTest3_4::inspectActiveSubroutineUniformName(GLuint program_id, const GLchar** uniform_names) const
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
bool result = true;
GLint n_active_subroutine_uniforms = 0;
std::vector<GLchar> active_uniform_name;
gl.getProgramStageiv(program_id, GL_VERTEX_SHADER, GL_ACTIVE_SUBROUTINE_UNIFORMS, &n_active_subroutine_uniforms);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramStageiv");
active_uniform_name.resize(m_n_active_subroutine_uniform_name_length + 1);
for (GLint uniform = 0; uniform < n_active_subroutine_uniforms; ++uniform)
{
bool is_name_ok = false;
gl.getActiveSubroutineUniformName(program_id, GL_VERTEX_SHADER, uniform, (GLsizei)active_uniform_name.size(),
0 /* length */, &active_uniform_name[0]);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetActiveSubroutineUniformName");
for (GLint name = 0; name < n_active_subroutine_uniforms; ++name)
{
if (0 == strcmp(uniform_names[name], &active_uniform_name[0]))
{
is_name_ok = true;
break;
}
}
if (false == is_name_ok)
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message
<< "Error. Invalid result. Function: getActiveSubroutineUniformName. idnex: " << uniform
<< ". Result: " << &active_uniform_name[0] << tcu::TestLog::EndMessage;
result = false;
break;
}
}
return result;
}
/** Test if getActiveSubroutineUniformName results are as expected
*
* @param program_id Program object id
* @param subroutine_names Array of subroutine names available in program
*
* @result false in case of invalid result, true otherwise
**/
bool FunctionalTest3_4::inspectActiveSubroutineName(GLuint program_id, const GLchar** subroutine_names) const
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
bool result = true;
GLint n_active_subroutines = 0;
std::vector<GLchar> active_subroutine_name;
gl.getProgramStageiv(program_id, GL_VERTEX_SHADER, GL_ACTIVE_SUBROUTINES, &n_active_subroutines);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramStageiv");
active_subroutine_name.resize(m_n_active_subroutine_name_length + 1);
for (GLint uniform = 0; uniform < n_active_subroutines; ++uniform)
{
bool is_name_ok = false;
gl.getActiveSubroutineName(program_id, GL_VERTEX_SHADER, uniform, (GLsizei)active_subroutine_name.size(),
0 /* length */, &active_subroutine_name[0]);
GLU_EXPECT_NO_ERROR(gl.getError(), "getActiveSubroutineName");
for (GLint name = 0; name < n_active_subroutines; ++name)
{
if (0 == strcmp(subroutine_names[name], &active_subroutine_name[0]))
{
is_name_ok = true;
break;
}
}
if (false == is_name_ok)
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message
<< "Error. Invalid result. Function: getActiveSubroutineName. idnex: " << uniform
<< ". Result: " << &active_subroutine_name[0] << tcu::TestLog::EndMessage;
result = false;
break;
}
}
return result;
}
/** Test if it is possible to "bind" all subroutines uniforms with all subroutines
*
* @param program_id Program object id
* @param subroutine_names Array of subroutine names available in program
* @param uniform_names Array of subroutine uniform names available in program
*
* @result false in case of invalid result, true otherwise
**/
bool FunctionalTest3_4::inspectSubroutineBinding(GLuint program_id, const GLchar** subroutine_names,
const GLchar** uniform_names, bool use_program_query) const
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
bool result = true;
GLint n_active_subroutines = 0;
GLint n_active_subroutine_uniforms = 0;
std::vector<GLuint> subroutine_uniforms;
GLuint queried_subroutine_index = 0;
gl.getProgramStageiv(program_id, GL_VERTEX_SHADER, GL_ACTIVE_SUBROUTINES, &n_active_subroutines);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramStageiv");
gl.getProgramStageiv(program_id, GL_VERTEX_SHADER, GL_ACTIVE_SUBROUTINE_UNIFORMS, &n_active_subroutine_uniforms);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramStageiv");
subroutine_uniforms.resize(n_active_subroutine_uniforms);
for (GLint uniform = 0; uniform < n_active_subroutine_uniforms; ++uniform)
{
GLuint uniform_location = getSubroutineUniformLocation(program_id, uniform_names[uniform], use_program_query);
for (GLint routine = 0; routine < n_active_subroutines; ++routine)
{
GLuint routine_index = getSubroutineIndex(program_id, subroutine_names[routine], use_program_query);
subroutine_uniforms[uniform] = routine_index;
gl.uniformSubroutinesuiv(GL_VERTEX_SHADER, n_active_subroutine_uniforms, &subroutine_uniforms[0]);
GLU_EXPECT_NO_ERROR(gl.getError(), "UniformSubroutinesuiv");
gl.getUniformSubroutineuiv(GL_VERTEX_SHADER, uniform_location, &queried_subroutine_index);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetUniformSubroutineuiv");
if (queried_subroutine_index != routine_index)
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "Error. Invalid result. Function: gl.getUniformSubroutineuiv."
<< " Subroutine uniform: " << uniform << ", name: " << uniform_names[uniform]
<< ", location: " << uniform_location << ". Subroutine: " << routine
<< ", name: " << subroutine_names[routine] << ", index: " << routine_index
<< ". Result: " << queried_subroutine_index << tcu::TestLog::EndMessage;
result = false;
}
}
}
return result;
}
/** Execute draw call and verify results
*
* @param program_id Program object id
* @param first_routine_name Name of subroutine that shall be used aas first_routine
* @param second_routine_name Name of subroutine that shall be used aas second_routine
* @param uniform_names Name of uniforms
* @param expected_results Test data. [0] is used as input data. All are used as expected_results
* @param use_program_query If true GetProgram* API will be used
*
* @return false in case of invalid result, true otherwise
**/
bool FunctionalTest3_4::testDraw(GLuint program_id, const GLchar* first_routine_name, const GLchar* second_routine_name,
const GLchar** uniform_names, const Utils::vec4<GLfloat> expected_results[5],
bool use_program_query) const
{
static const GLuint n_varyings = 5;
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
bool result = true;
GLuint subroutine_uniforms[2] = { 0 };
/* Get subroutine uniform locations */
GLint first_routine_location = getSubroutineUniformLocation(program_id, uniform_names[0], use_program_query);
GLint second_routine_location = getSubroutineUniformLocation(program_id, uniform_names[1], use_program_query);
/* Get subroutine indices */
GLuint first_routine_index = getSubroutineIndex(program_id, first_routine_name, use_program_query);
GLuint second_routine_index = getSubroutineIndex(program_id, second_routine_name, use_program_query);
/* Map uniforms with subroutines */
subroutine_uniforms[first_routine_location] = first_routine_index;
subroutine_uniforms[second_routine_location] = second_routine_index;
gl.uniformSubroutinesuiv(GL_VERTEX_SHADER, 2 /* number of uniforms */, &subroutine_uniforms[0]);
GLU_EXPECT_NO_ERROR(gl.getError(), "UniformSubroutinesuiv");
/* Get location of input_data */
GLint input_data_location = gl.getUniformLocation(program_id, "input_data");
GLU_EXPECT_NO_ERROR(gl.getError(), "GetUniformLocation");
if (-1 == input_data_location)
{
TCU_FAIL("Uniform is not available");
}
/* Set up input_data */
gl.uniform4f(input_data_location, expected_results[0].m_x, expected_results[0].m_y, expected_results[0].m_z,
expected_results[0].m_w);
GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform4f");
/* Execute draw call with transform feedback */
gl.beginTransformFeedback(GL_POINTS);
GLU_EXPECT_NO_ERROR(gl.getError(), "BeginTransformFeedback");
gl.drawArrays(GL_POINTS, 0 /* first */, 1 /* count */);
GLU_EXPECT_NO_ERROR(gl.getError(), "DrawArrays");
gl.endTransformFeedback();
GLU_EXPECT_NO_ERROR(gl.getError(), "EndTransformFeedback");
/* Verify results */
GLfloat* feedback_data = (GLfloat*)gl.mapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, GL_READ_ONLY);
GLU_EXPECT_NO_ERROR(gl.getError(), "MapBuffer");
Utils::vec4<GLfloat> results[5];
results[0].m_x = feedback_data[0];
results[0].m_y = feedback_data[1];
results[0].m_z = feedback_data[2];
results[0].m_w = feedback_data[3];
results[1].m_x = feedback_data[4];
results[1].m_y = feedback_data[5];
results[1].m_z = feedback_data[6];
results[1].m_w = feedback_data[7];
results[2].m_x = feedback_data[8];
results[2].m_y = feedback_data[9];
results[2].m_z = feedback_data[10];
results[2].m_w = feedback_data[11];
results[3].m_x = feedback_data[12];
results[3].m_y = feedback_data[13];
results[3].m_z = feedback_data[14];
results[3].m_w = feedback_data[15];
results[4].m_x = feedback_data[16];
results[4].m_y = feedback_data[17];
results[4].m_z = feedback_data[18];
results[4].m_w = feedback_data[19];
gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
GLU_EXPECT_NO_ERROR(gl.getError(), "UnmapBuffer");
for (GLuint i = 0; i < n_varyings; ++i)
{
result = result && (results[i] == expected_results[i]);
}
if (false == result)
{
m_context.getTestContext().getLog() << tcu::TestLog::Message
<< "Error. Invalid result. First routine: " << first_routine_name
<< ". Second routine: " << second_routine_name << tcu::TestLog::EndMessage;
tcu::MessageBuilder message = m_context.getTestContext().getLog() << tcu::TestLog::Message;
message << "Results:";
for (GLuint i = 0; i < n_varyings; ++i)
{
results[i].log(message);
}
message << tcu::TestLog::EndMessage;
message = m_context.getTestContext().getLog() << tcu::TestLog::Message;
message << "Expected:";
for (GLuint i = 0; i < n_varyings; ++i)
{
expected_results[i].log(message);
}
message << tcu::TestLog::EndMessage;
}
return result;
}
/** Constructor
*
* @param context CTS context
**/
FunctionalTest5::FunctionalTest5(deqp::Context& context)
: TestCase(context, "eight_subroutines_four_uniforms", "Verify multiple subroutine sets")
{
}
/** Execute test
*
* @return tcu::TestNode::STOP
**/
tcu::TestNode::IterateResult FunctionalTest5::iterate()
{
static const GLchar* vertex_shader_code =
"#version 400 core\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"precision highp float;\n"
"\n"
"// Subroutine types\n"
"subroutine vec4 routine_type_1(in vec4 left, in vec4 right);\n"
"subroutine vec4 routine_type_2(in vec4 iparam);\n"
"subroutine vec4 routine_type_3(in vec4 a, in vec4 b, in vec4 c);\n"
"subroutine bvec4 routine_type_4(in vec4 left, in vec4 right);\n"
"\n"
"// Subroutine definitions\n"
"// 1st type\n"
"subroutine(routine_type_1) vec4 add(in vec4 left, in vec4 right)\n"
"{\n"
" return left + right;\n"
"}\n"
"\n"
"subroutine(routine_type_1) vec4 subtract(in vec4 left, in vec4 right)\n"
"{\n"
" return left - right;\n"
"}\n"
"\n"
"// 2nd type\n"
"subroutine(routine_type_2) vec4 square(in vec4 iparam)\n"
"{\n"
" return iparam * iparam;\n"
"}\n"
"\n"
"subroutine(routine_type_2) vec4 square_root(in vec4 iparam)\n"
"{\n"
" return sqrt(iparam);\n"
"}\n"
"\n"
"// 3rd type\n"
"subroutine(routine_type_3) vec4 do_fma(in vec4 a, in vec4 b, in vec4 c)\n"
"{\n"
" return fma(a, b, c);\n"
"}\n"
"\n"
"subroutine(routine_type_3) vec4 blend(in vec4 a, in vec4 b, in vec4 c)\n"
"{\n"
" return c * a + (vec4(1) - c) * b;\n"
"}\n"
"\n"
"// 4th type\n"
"subroutine(routine_type_4) bvec4 are_equal(in vec4 left, in vec4 right)\n"
"{\n"
" return equal(left, right);\n"
"}\n"
"\n"
"subroutine(routine_type_4) bvec4 are_greater(in vec4 left, in vec4 right)\n"
"{\n"
" return greaterThan(left, right);\n"
"}\n"
"\n"
"// Sub routine uniforms\n"
"subroutine uniform routine_type_1 first_routine;\n"
"subroutine uniform routine_type_2 second_routine;\n"
"subroutine uniform routine_type_3 third_routine;\n"
"subroutine uniform routine_type_4 fourth_routine;\n"
"\n"
"// Input data\n"
"uniform vec4 first_input;\n"
"uniform vec4 second_input;\n"
"uniform vec4 third_input;\n"
"\n"
"// Output\n"
"out vec4 out_result_from_first_routine;\n"
"out vec4 out_result_from_second_routine;\n"
"out vec4 out_result_from_third_routine;\n"
"out uvec4 out_result_from_fourth_routine;\n"
"\n"
"void main()\n"
"{\n"
" out_result_from_first_routine = first_routine (first_input, second_input);\n"
" out_result_from_second_routine = second_routine(first_input);\n"
" out_result_from_third_routine = third_routine (first_input, second_input, third_input);\n"
" out_result_from_fourth_routine = uvec4(fourth_routine(first_input, second_input));\n"
"}\n"
"\n";
static const GLchar* subroutine_names[4][2] = {
{ "add", "subtract" }, { "square", "square_root" }, { "do_fma", "blend" }, { "are_equal", "are_greater" }
};
static const GLchar* subroutine_uniform_names[4][1] = {
{ "first_routine" }, { "second_routine" }, { "third_routine" }, { "fourth_routine" }
};
static const GLuint n_subroutine_types = sizeof(subroutine_names) / sizeof(subroutine_names[0]);
static const GLuint n_subroutines_per_type = sizeof(subroutine_names[0]) / sizeof(subroutine_names[0][0]);
static const GLuint n_subroutine_uniforms_per_type =
sizeof(subroutine_uniform_names[0]) / sizeof(subroutine_uniform_names[0][0]);
static const GLchar* uniform_names[] = { "first_input", "second_input", "third_input" };
static const GLuint n_uniform_names = sizeof(uniform_names) / sizeof(uniform_names[0]);
static const GLchar* varying_names[] = { "out_result_from_first_routine", "out_result_from_second_routine",
"out_result_from_third_routine", "out_result_from_fourth_routine" };
static const GLuint n_varyings = sizeof(varying_names) / sizeof(varying_names[0]);
static const GLuint transform_feedback_buffer_size = n_varyings * sizeof(GLfloat) * 4 /* vec4 */;
/* Test data */
static const Utils::vec4<GLfloat> input_data[3] = { Utils::vec4<GLfloat>(1.0f, 4.0f, 9.0f, 16.0f),
Utils::vec4<GLfloat>(16.0f, 9.0f, 4.0f, 1.0f),
Utils::vec4<GLfloat>(0.25f, 0.5f, 0.75f, 1.0f) };
static const Utils::vec4<GLfloat> expected_result_from_first_routine[2] = {
Utils::vec4<GLfloat>(17.0f, 13.0f, 13.0f, 17.0f), Utils::vec4<GLfloat>(-15.0f, -5.0f, 5.0f, 15.0f)
};
static const Utils::vec4<GLfloat> expected_result_from_second_routine[2] = {
Utils::vec4<GLfloat>(1.0f, 16.0f, 81.0f, 256.0f), Utils::vec4<GLfloat>(1.0f, 2.0f, 3.0f, 4.0f)
};
static const Utils::vec4<GLfloat> expected_result_from_third_routine[2] = {
Utils::vec4<GLfloat>(16.25f, 36.5f, 36.75f, 17.0f), Utils::vec4<GLfloat>(12.25f, 6.5f, 7.75f, 16.0f)
};
static const Utils::vec4<GLuint> expected_result_from_fourth_routine[2] = { Utils::vec4<GLuint>(0, 0, 0, 0),
Utils::vec4<GLuint>(0, 0, 1, 1) };
/* All combinations of subroutines */
static const GLuint subroutine_combinations[][4] = {
{ 0, 0, 0, 0 }, { 0, 0, 0, 1 }, { 0, 0, 1, 0 }, { 0, 0, 1, 1 }, { 0, 1, 0, 0 }, { 0, 1, 0, 1 },
{ 0, 1, 1, 0 }, { 0, 1, 1, 1 }, { 1, 0, 0, 0 }, { 1, 0, 0, 1 }, { 1, 0, 1, 0 }, { 1, 0, 1, 1 },
{ 1, 1, 0, 0 }, { 1, 1, 0, 1 }, { 1, 1, 1, 0 }, { 1, 1, 1, 1 }
};
static const GLuint n_subroutine_combinations =
sizeof(subroutine_combinations) / sizeof(subroutine_combinations[0]);
/* Do not execute the test if GL_ARB_shader_subroutine is not supported */
if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_shader_subroutine"))
{
throw tcu::NotSupportedError("GL_ARB_shader_subroutine is not supported.");
}
/* Result */
bool result = true;
/* GL objects */
Utils::program program(m_context);
Utils::buffer transform_feedback_buffer(m_context);
Utils::vertexArray vao(m_context);
/* Init GL objects */
program.build(0 /* cs */, 0 /* fs */, 0 /* gs */, 0 /* tcs */, 0 /* test */, vertex_shader_code, varying_names,
n_varyings);
program.use();
vao.generate();
vao.bind();
transform_feedback_buffer.generate();
transform_feedback_buffer.update(GL_TRANSFORM_FEEDBACK_BUFFER, transform_feedback_buffer_size, 0 /* data */,
GL_DYNAMIC_COPY);
transform_feedback_buffer.bindRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0, transform_feedback_buffer_size);
/* Get subroutine uniform locations and subroutine indices */
for (GLuint type = 0; type < n_subroutine_types; ++type)
{
for (GLuint uniform = 0; uniform < n_subroutine_uniforms_per_type; ++uniform)
{
m_subroutine_uniform_locations[type][uniform] =
program.getSubroutineUniformLocation(subroutine_uniform_names[type][uniform], GL_VERTEX_SHADER);
}
for (GLuint routine = 0; routine < n_subroutines_per_type; ++routine)
{
m_subroutine_indices[type][routine] =
program.getSubroutineIndex(subroutine_names[type][routine], GL_VERTEX_SHADER);
}
}
/* Get uniform locations */
for (GLuint i = 0; i < n_uniform_names; ++i)
{
m_uniform_locations[i] = program.getUniformLocation(uniform_names[i]);
}
/* Draw with each routine combination */
for (GLuint i = 0; i < n_subroutine_combinations; ++i)
{
Utils::vec4<GLfloat> first_routine_result;
Utils::vec4<GLfloat> second_routine_result;
Utils::vec4<GLfloat> third_routine_result;
Utils::vec4<GLuint> fourth_routine_result;
testDraw(subroutine_combinations[i], input_data, first_routine_result, second_routine_result,
third_routine_result, fourth_routine_result);
if (false == verify(first_routine_result, second_routine_result, third_routine_result, fourth_routine_result,
expected_result_from_first_routine[subroutine_combinations[i][0]],
expected_result_from_second_routine[subroutine_combinations[i][1]],
expected_result_from_third_routine[subroutine_combinations[i][2]],
expected_result_from_fourth_routine[subroutine_combinations[i][3]]))
{
logError(subroutine_names, subroutine_combinations[i], input_data, first_routine_result,
second_routine_result, third_routine_result, fourth_routine_result,
expected_result_from_first_routine[subroutine_combinations[i][0]],
expected_result_from_second_routine[subroutine_combinations[i][1]],
expected_result_from_third_routine[subroutine_combinations[i][2]],
expected_result_from_fourth_routine[subroutine_combinations[i][3]]);
result = false;
}
}
/* Done */
if (true == result)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
return tcu::TestNode::STOP;
}
/** Log error message
*
* @param subroutine_names Array of subroutine names
* @param subroutine_combination Combination of subroutines
* @param input_data Input data
* @param first_routine_result Result of first routine
* @param second_routine_result Result of second routine
* @param third_routine_result Result of third routine
* @param fourth_routine_result Result of fourth routine
* @param first_routine_expected_result Expected result of first routine
* @param second_routine_expected_result Expected result of second routine
* @param third_routine_expected_result Expected result of third routine
* @param fourth_routine_expected_result Expected result of fourth routine
**/
void FunctionalTest5::logError(const glw::GLchar* subroutine_names[4][2], const glw::GLuint subroutine_combination[4],
const Utils::vec4<glw::GLfloat> input_data[3],
const Utils::vec4<glw::GLfloat>& first_routine_result,
const Utils::vec4<glw::GLfloat>& second_routine_result,
const Utils::vec4<glw::GLfloat>& third_routine_result,
const Utils::vec4<glw::GLuint>& fourth_routine_result,
const Utils::vec4<glw::GLfloat>& first_routine_expected_result,
const Utils::vec4<glw::GLfloat>& second_routine_expected_result,
const Utils::vec4<glw::GLfloat>& third_routine_expected_result,
const Utils::vec4<glw::GLuint>& fourth_routine_expected_result) const
{
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Error. Invalid result."
<< tcu::TestLog::EndMessage;
tcu::MessageBuilder message = m_context.getTestContext().getLog() << tcu::TestLog::Message;
message << "Function: " << subroutine_names[0][subroutine_combination[0]] << "( ";
input_data[0].log(message);
message << ", ";
input_data[1].log(message);
message << " ). Result: ";
first_routine_result.log(message);
message << ". Expected: ";
first_routine_expected_result.log(message);
message << tcu::TestLog::EndMessage;
message = m_context.getTestContext().getLog() << tcu::TestLog::Message;
message << "Function: " << subroutine_names[1][subroutine_combination[1]] << "( ";
input_data[0].log(message);
message << " ). Result: ";
second_routine_result.log(message);
message << ". Expected: ";
second_routine_expected_result.log(message);
message << tcu::TestLog::EndMessage;
message = m_context.getTestContext().getLog() << tcu::TestLog::Message;
message << "Function: " << subroutine_names[2][subroutine_combination[2]] << "( ";
input_data[0].log(message);
message << ", ";
input_data[1].log(message);
message << ", ";
input_data[2].log(message);
message << "). Result: ";
third_routine_result.log(message);
message << ". Expected: ";
third_routine_expected_result.log(message);
message << tcu::TestLog::EndMessage;
message = m_context.getTestContext().getLog() << tcu::TestLog::Message;
message << "Function: " << subroutine_names[3][subroutine_combination[3]] << "( ";
input_data[0].log(message);
message << ", ";
input_data[1].log(message);
message << ", ";
message << " ). Result: ";
fourth_routine_result.log(message);
message << ". Expected: ";
fourth_routine_expected_result.log(message);
message << tcu::TestLog::EndMessage;
}
/** Execute draw call and capture results
*
* @param subroutine_combination Combination of subroutines
* @param input_data Input data
* @param out_first_routine_result Result of first routine
* @param out_second_routine_result Result of second routine
* @param out_third_routine_result Result of third routine
* @param out_fourth_routine_result Result of fourth routine
**/
void FunctionalTest5::testDraw(const glw::GLuint subroutine_combination[4],
const Utils::vec4<glw::GLfloat> input_data[3],
Utils::vec4<glw::GLfloat>& out_first_routine_result,
Utils::vec4<glw::GLfloat>& out_second_routine_result,
Utils::vec4<glw::GLfloat>& out_third_routine_result,
Utils::vec4<glw::GLuint>& out_fourth_routine_result) const
{
static const GLuint n_uniforms = sizeof(m_uniform_locations) / sizeof(m_uniform_locations[0]);
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
GLuint subroutine_indices[4];
static const GLuint n_subroutine_uniforms = sizeof(subroutine_indices) / sizeof(subroutine_indices[0]);
/* Prepare subroutine uniform data */
for (GLuint i = 0; i < n_subroutine_uniforms; ++i)
{
const GLuint location = m_subroutine_uniform_locations[i][0];
subroutine_indices[location] = m_subroutine_indices[i][subroutine_combination[i]];
}
/* Set up subroutine uniforms */
gl.uniformSubroutinesuiv(GL_VERTEX_SHADER, n_subroutine_uniforms, &subroutine_indices[0]);
GLU_EXPECT_NO_ERROR(gl.getError(), "UniformSubroutinesuiv");
/* Set up input data uniforms */
for (GLuint i = 0; i < n_uniforms; ++i)
{
gl.uniform4f(m_uniform_locations[i], input_data[i].m_x, input_data[i].m_y, input_data[i].m_z,
input_data[i].m_w);
GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform4f");
}
/* Execute draw call with transform feedback */
gl.beginTransformFeedback(GL_POINTS);
GLU_EXPECT_NO_ERROR(gl.getError(), "BeginTransformFeedback");
gl.drawArrays(GL_POINTS, 0 /* first */, 1 /* count */);
GLU_EXPECT_NO_ERROR(gl.getError(), "DrawArrays");
gl.endTransformFeedback();
GLU_EXPECT_NO_ERROR(gl.getError(), "EndTransformFeedback");
/* Capture results */
GLvoid* feedback_data = gl.mapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, GL_READ_ONLY);
GLU_EXPECT_NO_ERROR(gl.getError(), "MapBuffer");
GLfloat* float_ptr = (GLfloat*)feedback_data;
/* First result */
out_first_routine_result.m_x = float_ptr[0];
out_first_routine_result.m_y = float_ptr[1];
out_first_routine_result.m_z = float_ptr[2];
out_first_routine_result.m_w = float_ptr[3];
/* Second result */
out_second_routine_result.m_x = float_ptr[4];
out_second_routine_result.m_y = float_ptr[5];
out_second_routine_result.m_z = float_ptr[6];
out_second_routine_result.m_w = float_ptr[7];
/* Third result */
out_third_routine_result.m_x = float_ptr[8];
out_third_routine_result.m_y = float_ptr[9];
out_third_routine_result.m_z = float_ptr[10];
out_third_routine_result.m_w = float_ptr[11];
/* Fourth result */
GLuint* uint_ptr = (GLuint*)(float_ptr + 12);
out_fourth_routine_result.m_x = uint_ptr[0];
out_fourth_routine_result.m_y = uint_ptr[1];
out_fourth_routine_result.m_z = uint_ptr[2];
out_fourth_routine_result.m_w = uint_ptr[3];
gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
GLU_EXPECT_NO_ERROR(gl.getError(), "UnmapBuffer");
}
/** Verify if results match expected results
*
* @param first_routine_result Result of first routine
* @param second_routine_result Result of second routine
* @param third_routine_result Result of third routine
* @param fourth_routine_result Result of fourth routine
* @param first_routine_expected_result Expected result of first routine
* @param second_routine_expected_result Expected result of second routine
* @param third_routine_expected_result Expected result of third routine
* @param fourth_routine_expected_result Expected result of fourth routine
**/
bool FunctionalTest5::verify(const Utils::vec4<glw::GLfloat>& first_routine_result,
const Utils::vec4<glw::GLfloat>& second_routine_result,
const Utils::vec4<glw::GLfloat>& third_routine_result,
const Utils::vec4<glw::GLuint>& fourth_routine_result,
const Utils::vec4<glw::GLfloat>& first_routine_expected_result,
const Utils::vec4<glw::GLfloat>& second_routine_expected_result,
const Utils::vec4<glw::GLfloat>& third_routine_expected_result,
const Utils::vec4<glw::GLuint>& fourth_routine_expected_result) const
{
bool result = true;
result = result && (first_routine_result == first_routine_expected_result);
result = result && (second_routine_result == second_routine_expected_result);
result = result && (third_routine_result == third_routine_expected_result);
result = result && (fourth_routine_result == fourth_routine_expected_result);
return result;
}
/** Constructor
*
* @param context CTS context
**/
FunctionalTest6::FunctionalTest6(deqp::Context& context)
: TestCase(context, "static_subroutine_call", "Verify that subroutine can be called in a static manner")
{
}
/** Execute test
*
* @return tcu::TestNode::STOP
**/
tcu::TestNode::IterateResult FunctionalTest6::iterate()
{
static const GLchar* vertex_shader_code = "#version 400 core\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"precision highp float;\n"
"\n"
"// Subroutine type\n"
"subroutine vec4 routine_type(in vec4 iparam);\n"
"\n"
"// Subroutine definition\n"
"subroutine(routine_type) vec4 square(in vec4 iparam)\n"
"{\n"
" return iparam * iparam;\n"
"}\n"
"\n"
"// Sub routine uniform\n"
"subroutine uniform routine_type routine;\n"
"\n"
"// Input data\n"
"uniform vec4 input_data;\n"
"\n"
"// Output\n"
"out vec4 out_result;\n"
"\n"
"void main()\n"
"{\n"
" out_result = square(input_data);\n"
"}\n"
"\n";
static const GLchar* varying_name = "out_result";
/* Test data */
static const Utils::vec4<GLfloat> input_data(1.0f, 4.0f, 9.0f, 16.0f);
static const Utils::vec4<GLfloat> expected_result(1.0f, 16.0f, 81.0f, 256.0f);
static const GLuint transform_feedback_buffer_size = 4 * sizeof(GLfloat);
/* Do not execute the test if GL_ARB_shader_subroutine is not supported */
if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_shader_subroutine"))
{
throw tcu::NotSupportedError("GL_ARB_shader_subroutine is not supported.");
}
/* GL objects */
Utils::program program(m_context);
Utils::buffer transform_feedback_buffer(m_context);
Utils::vertexArray vao(m_context);
/* Init GL objects */
program.build(0 /* cs */, 0 /* fs */, 0 /* gs */, 0 /* tcs */, 0 /* test */, vertex_shader_code, &varying_name,
1 /* n_varyings */);
program.use();
vao.generate();
vao.bind();
transform_feedback_buffer.generate();
transform_feedback_buffer.update(GL_TRANSFORM_FEEDBACK_BUFFER, transform_feedback_buffer_size, 0 /* data */,
GL_DYNAMIC_COPY);
transform_feedback_buffer.bindRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0, transform_feedback_buffer_size);
/* Test */
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
const GLint uniform_location = gl.getUniformLocation(program.m_program_object_id, "input_data");
GLU_EXPECT_NO_ERROR(gl.getError(), "GetUniformLocation");
if (-1 == uniform_location)
{
TCU_FAIL("Uniform is not available");
}
/* Set up input data uniforms */
gl.uniform4f(uniform_location, input_data.m_x, input_data.m_y, input_data.m_z, input_data.m_w);
GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform4f");
/* Execute draw call with transform feedback */
gl.beginTransformFeedback(GL_POINTS);
GLU_EXPECT_NO_ERROR(gl.getError(), "BeginTransformFeedback");
gl.drawArrays(GL_POINTS, 0 /* first */, 1 /* count */);
GLU_EXPECT_NO_ERROR(gl.getError(), "DrawArrays");
gl.endTransformFeedback();
GLU_EXPECT_NO_ERROR(gl.getError(), "EndTransformFeedback");
/* Capture results */
GLfloat* feedback_data = (GLfloat*)gl.mapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, GL_READ_ONLY);
GLU_EXPECT_NO_ERROR(gl.getError(), "MapBuffer");
Utils::vec4<GLfloat> result(feedback_data[0], feedback_data[1], feedback_data[2], feedback_data[3]);
gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
GLU_EXPECT_NO_ERROR(gl.getError(), "UnmapBuffer");
/* Verify */
if (expected_result == result)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Error. Invalid result."
<< tcu::TestLog::EndMessage;
tcu::MessageBuilder message = m_context.getTestContext().getLog() << tcu::TestLog::Message;
message << "Function: square( ";
input_data.log(message);
message << " ). Result: ";
result.log(message);
message << ". Expected: ";
expected_result.log(message);
message << tcu::TestLog::EndMessage;
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
}
/* Done */
return tcu::TestNode::STOP;
}
/** Constructor
*
* @param context CTS context
**/
FunctionalTest7_8::FunctionalTest7_8(deqp::Context& context)
: TestCase(context, "arrayed_subroutine_uniforms", "Verify that subroutine can be called in a static manner")
{
}
/** Execute test
*
* @return tcu::TestNode::STOP
**/
tcu::TestNode::IterateResult FunctionalTest7_8::iterate()
{
static const GLchar* vertex_shader_code =
"#version 400 core\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"precision highp float;\n"
"\n"
"// Subroutine type\n"
"subroutine vec4 routine_type(in vec4 left, in vec4 right);\n"
"\n"
"// Subroutine definitions\n"
"subroutine(routine_type) vec4 add(in vec4 left, in vec4 right)\n"
"{\n"
" return left + right;\n"
"}\n"
"\n"
"subroutine(routine_type) vec4 multiply(in vec4 left, in vec4 right)\n"
"{\n"
" return left * right;\n"
"}\n"
"\n"
"// Sub routine uniform\n"
"subroutine uniform routine_type routine[4];\n"
"\n"
"// Input data\n"
"uniform vec4 uni_left;\n"
"uniform vec4 uni_right;\n"
"uniform uvec4 uni_indices;\n"
"\n"
"// Output\n"
"out vec4 out_combined;\n"
"out vec4 out_combined_inverted;\n"
"out vec4 out_constant;\n"
"out vec4 out_constant_inverted;\n"
"out vec4 out_dynamic;\n"
"out vec4 out_dynamic_inverted;\n"
"out vec4 out_loop;\n"
"out uint out_array_length;\n"
"\n"
"void main()\n"
"{\n"
" out_combined = routine[3](routine[2](routine[1](routine[0](uni_left, uni_right), uni_right), "
"uni_right), uni_right);\n"
" out_combined_inverted = routine[0](routine[1](routine[2](routine[3](uni_left, uni_right), uni_right), "
"uni_right), uni_right);\n"
" \n"
" out_constant = routine[3](routine[2](routine[1](routine[0](vec4(1, 2, 3, 4), vec4(-5, -6, -7, "
"-8)), vec4(-1, -2, -3, -4)), vec4(5, 6, 7, 8)), vec4(1, 2, 3, 4));\n"
" out_constant_inverted = routine[0](routine[1](routine[2](routine[3](vec4(1, 2, 3, 4), vec4(-5, -6, -7, "
"-8)), vec4(-1, -2, -3, -4)), vec4(5, 6, 7, 8)), vec4(1, 2, 3, 4));\n"
" \n"
" out_dynamic = "
"routine[uni_indices.w](routine[uni_indices.z](routine[uni_indices.y](routine[uni_indices.x](uni_left, "
"uni_right), uni_right), uni_right), uni_right);\n"
" out_dynamic_inverted = "
"routine[uni_indices.x](routine[uni_indices.y](routine[uni_indices.z](routine[uni_indices.w](uni_left, "
"uni_right), uni_right), uni_right), uni_right);\n"
" \n"
" out_loop = uni_left;\n"
" for (uint i = 0u; i < routine.length(); ++i)\n"
" {\n"
" out_loop = routine[i](out_loop, uni_right);\n"
" }\n"
" \n"
" out_array_length = routine.length() + 6 - (uni_indices.x + uni_indices.y + uni_indices.z + "
"uni_indices.w);\n"
"}\n"
"\n";
static const GLchar* subroutine_names[] = {
"add", "multiply",
};
static const GLuint n_subroutine_names = sizeof(subroutine_names) / sizeof(subroutine_names[0]);
static const GLchar* subroutine_uniform_names[] = { "routine[0]", "routine[1]", "routine[2]", "routine[3]" };
static const GLuint n_subroutine_uniform_names =
sizeof(subroutine_uniform_names) / sizeof(subroutine_uniform_names[0]);
static const GLchar* uniform_names[] = {
"uni_left", "uni_right", "uni_indices",
};
static const GLuint n_uniform_names = sizeof(uniform_names) / sizeof(uniform_names[0]);
static const GLchar* varying_names[] = { "out_combined", "out_combined_inverted",
"out_constant", "out_constant_inverted",
"out_dynamic", "out_dynamic_inverted",
"out_loop", "out_array_length" };
static const GLuint n_varyings = sizeof(varying_names) / sizeof(varying_names[0]);
static const GLuint transform_feedback_buffer_size = n_varyings * 4 * sizeof(GLfloat);
/* Test data */
static const Utils::vec4<GLfloat> uni_left(-1.0f, 0.75f, -0.5f, 0.25f);
static const Utils::vec4<GLfloat> uni_right(1.0f, -0.75f, 0.5f, -0.25f);
static const Utils::vec4<GLuint> uni_indices(1, 2, 0, 3);
static const GLuint subroutine_combinations[][4] = {
{ 0, 0, 0, 0 }, /* + + + + */
{ 0, 0, 0, 1 }, /* + + + * */
{ 0, 0, 1, 0 }, /* + + * + */
{ 0, 0, 1, 1 }, /* + + * * */
{ 0, 1, 0, 0 }, /* + * + + */
{ 0, 1, 0, 1 }, /* + * + * */
{ 0, 1, 1, 0 }, /* + * * + */
{ 0, 1, 1, 1 }, /* + * * * */
{ 1, 0, 0, 0 }, /* * + + + */
{ 1, 0, 0, 1 }, /* * + + * */
{ 1, 0, 1, 0 }, /* * + * + */
{ 1, 0, 1, 1 }, /* * + * * */
{ 1, 1, 0, 0 }, /* * * + + */
{ 1, 1, 0, 1 }, /* * * + * */
{ 1, 1, 1, 0 }, /* * * * + */
{ 1, 1, 1, 1 } /* * * * * */
};
static const GLuint n_subroutine_combinations =
sizeof(subroutine_combinations) / sizeof(subroutine_combinations[0]);
/* Do not execute the test if GL_ARB_shader_subroutine is not supported */
if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_shader_subroutine"))
{
throw tcu::NotSupportedError("GL_ARB_shader_subroutine is not supported.");
}
/* GL objects */
Utils::program program(m_context);
Utils::buffer transform_feedback_buffer(m_context);
Utils::vertexArray vao(m_context);
bool result = true;
/* Init GL objects */
program.build(0 /* cs */, 0 /* fs */, 0 /* gs */, 0 /* tcs */, 0 /* test */, vertex_shader_code, varying_names,
n_varyings);
program.use();
vao.generate();
vao.bind();
transform_feedback_buffer.generate();
/* Get subroutine indices */
for (GLuint routine = 0; routine < n_subroutine_names; ++routine)
{
m_subroutine_indices[routine] = program.getSubroutineIndex(subroutine_names[routine], GL_VERTEX_SHADER);
}
/* Get subroutine uniform locations */
for (GLuint uniform = 0; uniform < n_subroutine_uniform_names; ++uniform)
{
m_subroutine_uniform_locations[uniform] =
program.getSubroutineUniformLocation(subroutine_uniform_names[uniform], GL_VERTEX_SHADER);
}
/* Get uniform locations */
for (GLuint i = 0; i < n_uniform_names; ++i)
{
m_uniform_locations[i] = program.getUniformLocation(uniform_names[i]);
}
/* Test */
for (GLuint i = 0; i < n_subroutine_combinations; ++i)
{
/* Clean */
transform_feedback_buffer.update(GL_TRANSFORM_FEEDBACK_BUFFER, transform_feedback_buffer_size, 0 /* data */,
GL_DYNAMIC_COPY);
transform_feedback_buffer.bindRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0, transform_feedback_buffer_size);
/* Verify */
if (false == testDraw(subroutine_combinations[i], uni_left, uni_right, uni_indices))
{
result = false;
}
}
if (true == result)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
/* Done */
return tcu::TestNode::STOP;
}
/* Calculate result of function applied to operands
*
* @param function Function id, 0 is sum, 1 is multiplication
* @param left Left operand
* @param right Right operand
* @param out Function result
**/
void FunctionalTest7_8::calculate(glw::GLuint function, const Utils::vec4<glw::GLfloat>& left,
const Utils::vec4<glw::GLfloat>& right, Utils::vec4<glw::GLfloat>& out) const
{
if (0 == function)
{
out.m_x = left.m_x + right.m_x;
out.m_y = left.m_y + right.m_y;
out.m_z = left.m_z + right.m_z;
out.m_w = left.m_w + right.m_w;
}
else
{
out.m_x = left.m_x * right.m_x;
out.m_y = left.m_y * right.m_y;
out.m_z = left.m_z * right.m_z;
out.m_w = left.m_w * right.m_w;
}
}
/** Calculate expected values for all operations
*
* @param combination Function combination, first applied function is at index [0]
* @param left Left operand
* @param right Right operand
* @param indices Indices used by dynamic calls
* @param out_combined Expected result of "combined" operation
* @param out_combined_inverted Expected result of "combined_inverted" operation
* @param out_constant Expected result of "constant" operation
* @param out_constant_inverted Expected result of "constant_inverted" operation
* @param out_dynamic Expected result of "dynamic" operation
* @param out_dynamic_inverted Expected result of "out_dynamic_inverted" operation
* @param out_loop Expected result of "loop" operation
**/
void FunctionalTest7_8::calculate(
const glw::GLuint combination[4], const Utils::vec4<glw::GLfloat>& left, const Utils::vec4<glw::GLfloat>& right,
const Utils::vec4<glw::GLuint>& indices, Utils::vec4<glw::GLfloat>& out_combined,
Utils::vec4<glw::GLfloat>& out_combined_inverted, Utils::vec4<glw::GLfloat>& out_constant,
Utils::vec4<glw::GLfloat>& out_constant_inverted, Utils::vec4<glw::GLfloat>& out_dynamic,
Utils::vec4<glw::GLfloat>& out_dynamic_inverted, Utils::vec4<glw::GLfloat>& out_loop) const
{
/* Indices used by "dynamic" operations, range <0..4> */
const GLuint dynamic_combination[4] = { combination[indices.m_x], combination[indices.m_y],
combination[indices.m_z], combination[indices.m_w] };
/* Values used by "constant" operations, come from shader code */
const Utils::vec4<glw::GLfloat> constant_values[] = { Utils::vec4<glw::GLfloat>(1, 2, 3, 4),
Utils::vec4<glw::GLfloat>(-5, -6, -7, -8),
Utils::vec4<glw::GLfloat>(-1, -2, -3, -4),
Utils::vec4<glw::GLfloat>(5, 6, 7, 8),
Utils::vec4<glw::GLfloat>(1, 2, 3, 4) };
/* Start values */
Utils::vec4<glw::GLfloat> combined = left;
Utils::vec4<glw::GLfloat> combined_inverted = left;
Utils::vec4<glw::GLfloat> constant = constant_values[0];
Utils::vec4<glw::GLfloat> constant_inverted = constant_values[0];
Utils::vec4<glw::GLfloat> dynamic = left;
Utils::vec4<glw::GLfloat> dynamic_inverted = left;
/* Calculate expected results */
for (GLuint i = 0; i < 4; ++i)
{
GLuint function = combination[i];
GLuint function_inverted = combination[3 - i];
GLuint dynamic_function = dynamic_combination[i];
GLuint dynamic_function_inverted = dynamic_combination[3 - i];
calculate(function, combined, right, combined);
calculate(function_inverted, combined_inverted, right, combined_inverted);
calculate(function, constant, constant_values[i + 1], constant);
calculate(function_inverted, constant_inverted, constant_values[i + 1], constant_inverted);
calculate(dynamic_function, dynamic, right, dynamic);
calculate(dynamic_function_inverted, dynamic_inverted, right, dynamic_inverted);
}
/* Store results */
out_combined = combined;
out_combined_inverted = combined_inverted;
out_constant = constant;
out_constant_inverted = constant_inverted;
out_dynamic = dynamic;
out_dynamic_inverted = dynamic_inverted;
out_loop = combined;
}
/** Log error
*
* @param combination Operations combination
* @param left Left operand
* @param right Right operand
* @param indices Inidices used by "dynamic" calls
* @param vec4_expected Expected results
* @param vec4_result Results
* @param array_length Length of array
* @param result Comparison results
**/
void FunctionalTest7_8::logError(const glw::GLuint combination[4], const Utils::vec4<glw::GLfloat>& left,
const Utils::vec4<glw::GLfloat>& right, const Utils::vec4<glw::GLuint>& indices,
const Utils::vec4<glw::GLfloat> vec4_expected[7],
const Utils::vec4<glw::GLfloat> vec4_result[7], glw::GLuint array_length,
bool result[7]) const
{
static const GLuint n_functions = 4;
static const GLuint n_operations = 7;
/* Indices used by "dynamic" operations, range <0..4> */
const GLuint dynamic_combination[4] = { combination[indices.m_x], combination[indices.m_y],
combination[indices.m_z], combination[indices.m_w] };
/* Function symbols */
GLchar functions[4];
GLchar functions_inverted[4];
GLchar functions_dynamic[4];
GLchar functions_dynamic_inverted[4];
for (GLuint i = 0; i < n_functions; ++i)
{
GLchar function = (0 == combination[i]) ? '+' : '*';
GLchar dynamic_function = (0 == dynamic_combination[i]) ? '+' : '*';
functions[i] = function;
functions_inverted[n_functions - i - 1] = function;
functions_dynamic[i] = dynamic_function;
functions_dynamic_inverted[n_functions - i - 1] = dynamic_function;
}
/* Values used by "constant" operations, come from shader code */
const Utils::vec4<glw::GLfloat> constant_values[] = { Utils::vec4<glw::GLfloat>(1, 2, 3, 4),
Utils::vec4<glw::GLfloat>(-5, -6, -7, -8),
Utils::vec4<glw::GLfloat>(-1, -2, -3, -4),
Utils::vec4<glw::GLfloat>(5, 6, 7, 8),
Utils::vec4<glw::GLfloat>(1, 2, 3, 4) };
/* Values used by non-"constant" operations */
Utils::vec4<glw::GLfloat> dynamic_values[5];
dynamic_values[0] = left;
dynamic_values[1] = right;
dynamic_values[2] = right;
dynamic_values[3] = right;
dynamic_values[4] = right;
/* For each operation */
for (GLuint i = 0; i < n_operations; ++i)
{
/* If result is failure */
if (false == result[i])
{
const GLchar* description = 0;
const Utils::vec4<glw::GLfloat>* input = 0;
const GLchar* operation = 0;
switch (i)
{
case 0:
description = "Call made with predefined array indices";
input = dynamic_values;
operation = functions;
break;
case 1:
description = "Call made with predefined array indices in inverted order";
input = dynamic_values;
operation = functions_inverted;
break;
case 2:
description = "Call made with predefined array indices, for constant values";
input = constant_values;
operation = functions;
break;
case 3:
description = "Call made with predefined array indices in inverted order, for constant values";
input = constant_values;
operation = functions_inverted;
break;
case 4:
description = "Call made with dynamic array indices";
input = dynamic_values;
operation = functions_dynamic;
break;
case 5:
description = "Call made with dynamic array indices in inverted order";
input = dynamic_values;
operation = functions_dynamic_inverted;
break;
case 6:
description = "Call made with loop";
input = dynamic_values;
operation = functions;
break;
}
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Error. Invalid result."
<< tcu::TestLog::EndMessage;
m_context.getTestContext().getLog() << tcu::TestLog::Message << description << tcu::TestLog::EndMessage;
tcu::MessageBuilder message = m_context.getTestContext().getLog() << tcu::TestLog::Message;
message << "Operation: ((((";
input[0].log(message);
for (GLuint function = 0; function < n_functions; ++function)
{
message << " " << operation[function] << " ";
input[function + 1].log(message);
message << ")";
}
message << tcu::TestLog::EndMessage;
message = m_context.getTestContext().getLog() << tcu::TestLog::Message;
message << "Result: ";
vec4_result[i].log(message);
message << tcu::TestLog::EndMessage;
message = m_context.getTestContext().getLog() << tcu::TestLog::Message;
message << "Expected: ";
vec4_expected[i].log(message);
message << tcu::TestLog::EndMessage;
}
/* Check array length, it should be 4 */
if (4 != array_length)
{
m_context.getTestContext().getLog() << tcu::TestLog::Message
<< "Error. Invalid array length: " << array_length << ". Expected 4."
<< tcu::TestLog::EndMessage;
}
}
}
/** Execute draw call and verifies captrued varyings
*
* @param combination Function combination, first applied function is at index [0]
* @param left Left operand
* @param right Right operand
* @param indices Indices used by dynamic calls
*
* @return true if all results match expected values, false otherwise
**/
bool FunctionalTest7_8::testDraw(const glw::GLuint combination[4], const Utils::vec4<glw::GLfloat>& left,
const Utils::vec4<glw::GLfloat>& right, const Utils::vec4<glw::GLuint>& indices) const
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
static const GLuint n_vec4_varyings = 7;
bool result = true;
GLuint subroutine_indices[4];
static const GLuint n_subroutine_uniforms = sizeof(subroutine_indices) / sizeof(subroutine_indices[0]);
/* Prepare expected results */
Utils::vec4<glw::GLfloat> expected_results[7];
calculate(combination, left, right, indices, expected_results[0], expected_results[1], expected_results[2],
expected_results[3], expected_results[4], expected_results[5], expected_results[6]);
/* Set up input data uniforms */
gl.uniform4f(m_uniform_locations[0], left.m_x, left.m_y, left.m_z, left.m_w);
GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform4f");
gl.uniform4f(m_uniform_locations[1], right.m_x, right.m_y, right.m_z, right.m_w);
GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform4f");
gl.uniform4ui(m_uniform_locations[2], indices.m_x, indices.m_y, indices.m_z, indices.m_w);
GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform4ui");
/* Prepare subroutine uniform data */
for (GLuint i = 0; i < n_subroutine_uniforms; ++i)
{
const GLuint location = m_subroutine_uniform_locations[i];
subroutine_indices[location] = m_subroutine_indices[combination[i]];
}
/* Set up subroutine uniforms */
gl.uniformSubroutinesuiv(GL_VERTEX_SHADER, n_subroutine_uniforms, &subroutine_indices[0]);
GLU_EXPECT_NO_ERROR(gl.getError(), "UniformSubroutinesuiv");
/* Execute draw call with transform feedback */
gl.beginTransformFeedback(GL_POINTS);
GLU_EXPECT_NO_ERROR(gl.getError(), "BeginTransformFeedback");
gl.drawArrays(GL_POINTS, 0 /* first */, 1 /* count */);
GLU_EXPECT_NO_ERROR(gl.getError(), "DrawArrays");
gl.endTransformFeedback();
GLU_EXPECT_NO_ERROR(gl.getError(), "EndTransformFeedback");
/* Capture results */
GLvoid* feedback_data = (GLvoid*)gl.mapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, GL_READ_ONLY);
GLU_EXPECT_NO_ERROR(gl.getError(), "MapBuffer");
Utils::vec4<GLfloat> vec4_results[7];
bool results[7];
GLfloat* float_data = (GLfloat*)feedback_data;
for (GLuint i = 0; i < n_vec4_varyings; ++i)
{
vec4_results[i].m_x = float_data[i * 4 + 0];
vec4_results[i].m_y = float_data[i * 4 + 1];
vec4_results[i].m_z = float_data[i * 4 + 2];
vec4_results[i].m_w = float_data[i * 4 + 3];
}
GLuint* uint_data = (GLuint*)(float_data + (n_vec4_varyings)*4);
GLuint array_length = uint_data[0];
/* Unmap buffer */
gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
GLU_EXPECT_NO_ERROR(gl.getError(), "UnmapBuffer");
/* Verification */
for (GLuint i = 0; i < n_vec4_varyings; ++i)
{
results[i] = (vec4_results[i] == expected_results[i]);
result = result && results[i];
}
result = result && (4 == array_length);
/* Log error if any */
if (false == result)
{
logError(combination, left, right, indices, expected_results, vec4_results, array_length, results);
}
/* Done */
return result;
}
/** Constructor.
*
* @param context Rendering context.
*
**/
FunctionalTest9::FunctionalTest9(deqp::Context& context)
: TestCase(context, "subroutines_3_subroutine_types_and_subroutine_uniforms_one_function",
"Makes sure that program with one function associated with 3 different "
"subroutine types and 3 subroutine uniforms using that function compiles "
"and works as expected")
, m_has_test_passed(true)
, m_n_points_to_draw(16) /* arbitrary value */
, m_po_id(0)
, m_vao_id(0)
, m_vs_id(0)
, m_xfb_bo_id(0)
{
/* Left blank intentionally */
}
/** De-initializes GL objects that may have been created during test execution. */
void FunctionalTest9::deinit()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
if (m_po_id != 0)
{
gl.deleteProgram(m_po_id);
m_po_id = 0;
}
if (m_vao_id != 0)
{
gl.deleteVertexArrays(1, &m_vao_id);
m_vao_id = 0;
}
if (m_vs_id != 0)
{
gl.deleteShader(m_vs_id);
m_vs_id = 0;
}
if (m_xfb_bo_id != 0)
{
gl.deleteBuffers(1, &m_xfb_bo_id);
m_xfb_bo_id = 0;
}
}
/** Retrieves body of a vertex shader that should be used
* for the testing purposes.
**/
std::string FunctionalTest9::getVertexShaderBody() const
{
return "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"subroutine void subroutineType1(inout float);\n"
"subroutine void subroutineType2(inout float);\n"
"subroutine void subroutineType3(inout float);\n"
"\n"
"subroutine(subroutineType1, subroutineType2, subroutineType3) void function(inout float result)\n"
"{\n"
" result += float(0.123) + float(gl_VertexID);\n"
"}\n"
"\n"
"subroutine uniform subroutineType1 subroutine_uniform1;\n"
"subroutine uniform subroutineType2 subroutine_uniform2;\n"
"subroutine uniform subroutineType3 subroutine_uniform3;\n"
"\n"
"out vec4 result;\n"
"\n"
"void main()\n"
"{\n"
" result = vec4(0, 1, 2, 3);\n"
"\n"
" subroutine_uniform1(result.x);\n"
" subroutine_uniform2(result.y);\n"
" subroutine_uniform3(result.z);\n"
"\n"
" result.w += result.x + result.y + result.z;\n"
"}\n";
}
/** Initializes all GL objects required to run the test. */
void FunctionalTest9::initTest()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Set up program object */
const char* xfb_varyings[] = { "result" };
const unsigned int n_xfb_varyings = sizeof(xfb_varyings) / sizeof(xfb_varyings[0]);
if (!Utils::buildProgram(gl, getVertexShaderBody(), "", /* tc_body */
"", /* te_body */
"", /* gs_body */
"", /* fs_body */
xfb_varyings, n_xfb_varyings, &m_vs_id, DE_NULL, /* out_tc_id */
DE_NULL, /* out_te_id */
DE_NULL, /* out_gs_id */
DE_NULL, /* out_fs_id */
&m_po_id))
{
TCU_FAIL("Program failed to link successfully");
}
/* Set up a buffer object we will use to hold XFB data */
const unsigned int xfb_bo_size = static_cast<unsigned int>(sizeof(float) * 4 /* components */ * m_n_points_to_draw);
gl.genBuffers(1, &m_xfb_bo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers() call failed.");
gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_xfb_bo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call failed.");
gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* index */, m_xfb_bo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase() call failed.");
gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, xfb_bo_size, DE_NULL, /* data */
GL_STATIC_COPY);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() call failed.");
/* Generate & bind a VAO */
gl.genVertexArrays(1, &m_vao_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenVertexArrays() call failed.");
gl.bindVertexArray(m_vao_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray() call failed.");
}
/** Executes test iteration.
*
* @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed.
*/
tcu::TestNode::IterateResult FunctionalTest9::iterate()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Do not execute the test if GL_ARB_shader_subroutine is not supported */
if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_shader_subroutine"))
{
throw tcu::NotSupportedError("GL_ARB_shader_subroutine is not supported.");
}
initTest();
/* Issue a draw call to make use of the three subroutine uniforms that we've defined */
gl.useProgram(m_po_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() call failed.");
gl.beginTransformFeedback(GL_POINTS);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback() call failed.");
{
gl.drawArrays(GL_POINTS, 0 /* first */, m_n_points_to_draw);
GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays() call failed.");
}
gl.endTransformFeedback();
GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback() call failed.");
/* Map the XFB BO storage into process space */
const glw::GLvoid* xfb_data_ptr = gl.mapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, GL_READ_ONLY);
GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBuffer() call failed.");
verifyXFBData(xfb_data_ptr);
gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer() call failed.");
/* All done */
if (m_has_test_passed)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
return STOP;
}
/** Verifies the data XFBed out by the vertex shader. Should the data
* be found invalid, m_has_test_passed will be set to false.
*
* @param data_ptr XFB data.
**/
void FunctionalTest9::verifyXFBData(const glw::GLvoid* data_ptr)
{
const float epsilon = 1e-5f;
bool should_continue = true;
const glw::GLfloat* traveller_ptr = (const glw::GLfloat*)data_ptr;
for (unsigned int n_point = 0; n_point < m_n_points_to_draw && should_continue; ++n_point)
{
tcu::Vec4 expected_result(0, 1, 2, 3);
for (unsigned int n_component = 0; n_component < 3 /* xyz */; ++n_component)
{
expected_result[n_component] += 0.123f + float(n_point);
}
expected_result[3 /* w */] += expected_result[0] + expected_result[1] + expected_result[2];
if (de::abs(expected_result[0] - traveller_ptr[0]) > epsilon ||
de::abs(expected_result[1] - traveller_ptr[1]) > epsilon ||
de::abs(expected_result[2] - traveller_ptr[2]) > epsilon ||
de::abs(expected_result[3] - traveller_ptr[3]) > epsilon)
{
m_testCtx.getLog() << tcu::TestLog::Message << "XFBed data is invalid. Expected:"
"("
<< expected_result[0] << ", " << expected_result[1] << ", " << expected_result[2] << ", "
<< expected_result[3] << "), found:(" << traveller_ptr[0] << ", " << traveller_ptr[1]
<< ", " << traveller_ptr[2] << ", " << traveller_ptr[3] << ")."
<< tcu::TestLog::EndMessage;
m_has_test_passed = false;
should_continue = false;
}
traveller_ptr += 4; /* xyzw */
} /* for (all rendered points) */
}
/** Constructor
*
* @param context CTS context
**/
FunctionalTest10::FunctionalTest10(deqp::Context& context)
: TestCase(context, "arrays_of_arrays_of_uniforms", "Verify that arrays of arrays of uniforms works as expected")
{
}
/** Execute test
*
* @return tcu::TestNode::STOP
**/
tcu::TestNode::IterateResult FunctionalTest10::iterate()
{
static const GLchar* vertex_shader_code = "#version 400 core\n"
"#extension GL_ARB_arrays_of_arrays : require\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"precision highp float;\n"
"\n"
"// Subroutine type\n"
"subroutine int routine_type(in int iparam);\n"
"\n"
"// Subroutine definitions\n"
"subroutine(routine_type) int increment(in int iparam)\n"
"{\n"
" return iparam + 1;\n"
"}\n"
"\n"
"subroutine(routine_type) int decrement(in int iparam)\n"
"{\n"
" return iparam - 1;\n"
"}\n"
"\n"
"// Sub routine uniform\n"
"subroutine uniform routine_type routine[4][4];\n"
"\n"
"// Output\n"
"out int out_result;\n"
"\n"
"void main()\n"
"{\n"
" int result = 0;\n"
" \n"
" for (uint j = 0; j < routine.length(); ++j)\n"
" {\n"
" for (uint i = 0; i < routine[j].length(); ++i)\n"
" {\n"
" result = routine[j][i](result);\n"
" }\n"
" }\n"
" \n"
" out_result = result;\n"
"}\n"
"\n";
static const GLchar* subroutine_names[] = {
"increment", "decrement",
};
static const GLuint n_subroutine_names = sizeof(subroutine_names) / sizeof(subroutine_names[0]);
static const GLchar* subroutine_uniform_names[] = {
"routine[0][0]", "routine[1][0]", "routine[2][0]", "routine[3][0]", "routine[0][1]", "routine[1][1]",
"routine[2][1]", "routine[3][1]", "routine[0][2]", "routine[1][2]", "routine[2][2]", "routine[3][2]",
"routine[0][3]", "routine[1][3]", "routine[2][3]", "routine[3][3]"
};
static const GLuint n_subroutine_uniform_names =
sizeof(subroutine_uniform_names) / sizeof(subroutine_uniform_names[0]);
static const GLchar* varying_name = "out_result";
static const GLuint transform_feedback_buffer_size = sizeof(GLint);
static const GLuint configuration_increment[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
static const GLuint configuration_decrement[16] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
static const GLuint configuration_mix[16] = { 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1 };
/* Do not execute the test if GL_ARB_shader_subroutine is not supported */
if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_shader_subroutine"))
{
throw tcu::NotSupportedError("GL_ARB_shader_subroutine is not supported.");
}
/* Do not execute the test if GL_ARB_arrays_of_arrays is not supported */
if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_arrays_of_arrays"))
{
throw tcu::NotSupportedError("GL_ARB_arrays_of_arrays is not supported.");
}
bool result = true;
/* GL objects */
Utils::program program(m_context);
Utils::buffer transform_feedback_buffer(m_context);
Utils::vertexArray vao(m_context);
/* Init GL objects */
program.build(0 /* cs */, 0 /* fs */, 0 /* gs */, 0 /* tcs */, 0 /* test */, vertex_shader_code, &varying_name,
1 /* n_varyings */);
program.use();
vao.generate();
vao.bind();
transform_feedback_buffer.generate();
transform_feedback_buffer.update(GL_TRANSFORM_FEEDBACK_BUFFER, transform_feedback_buffer_size, 0 /* data */,
GL_DYNAMIC_COPY);
transform_feedback_buffer.bindRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0, transform_feedback_buffer_size);
/* Get subroutine indices */
for (GLuint routine = 0; routine < n_subroutine_names; ++routine)
{
m_subroutine_indices[routine] = program.getSubroutineIndex(subroutine_names[routine], GL_VERTEX_SHADER);
}
/* Get subroutine uniform locations */
for (GLuint uniform = 0; uniform < n_subroutine_uniform_names; ++uniform)
{
m_subroutine_uniform_locations[uniform] =
program.getSubroutineUniformLocation(subroutine_uniform_names[uniform], GL_VERTEX_SHADER);
}
/* Test */
GLint increment_result = testDraw(configuration_increment);
GLint decrement_result = testDraw(configuration_decrement);
GLint mix_result = testDraw(configuration_mix);
/* Verify */
if (16 != increment_result)
{
result = false;
}
if (-16 != decrement_result)
{
result = false;
}
if (0 != mix_result)
{
result = false;
}
/* Set test result */
if (true == result)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Error. Invalid result."
<< " Incrementation applied 16 times: " << increment_result
<< ". Decrementation applied 16 times: " << decrement_result
<< ". Incrementation and decrementation applied 8 times: " << mix_result
<< tcu::TestLog::EndMessage;
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
/* Done */
return tcu::TestNode::STOP;
}
/** Execute draw call and return captured varying
*
* @param routine_indices Configuration of subroutine uniforms
*
* @return Value of varying captured with transform feedback
**/
GLint FunctionalTest10::testDraw(const GLuint routine_indices[16]) const
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
GLuint subroutine_indices[16];
static const GLuint n_subroutine_uniforms = sizeof(subroutine_indices) / sizeof(subroutine_indices[0]);
/* Prepare subroutine uniform data */
for (GLuint i = 0; i < n_subroutine_uniforms; ++i)
{
const GLuint location = m_subroutine_uniform_locations[i];
subroutine_indices[location] = m_subroutine_indices[routine_indices[i]];
}
/* Set up subroutine uniforms */
gl.uniformSubroutinesuiv(GL_VERTEX_SHADER, n_subroutine_uniforms, &subroutine_indices[0]);
GLU_EXPECT_NO_ERROR(gl.getError(), "UniformSubroutinesuiv");
/* Execute draw call with transform feedback */
gl.beginTransformFeedback(GL_POINTS);
GLU_EXPECT_NO_ERROR(gl.getError(), "BeginTransformFeedback");
gl.drawArrays(GL_POINTS, 0 /* first */, 1 /* count */);
GLU_EXPECT_NO_ERROR(gl.getError(), "DrawArrays");
gl.endTransformFeedback();
GLU_EXPECT_NO_ERROR(gl.getError(), "EndTransformFeedback");
/* Capture results */
GLint* feedback_data = (GLint*)gl.mapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, GL_READ_ONLY);
GLU_EXPECT_NO_ERROR(gl.getError(), "MapBuffer");
GLint result = feedback_data[0];
/* Unmap buffer */
gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
GLU_EXPECT_NO_ERROR(gl.getError(), "UnmapBuffer");
return result;
}
/* Definitions of constants used by FunctionalTest11 */
const GLuint FunctionalTest11::m_texture_height = 32;
const GLuint FunctionalTest11::m_texture_width = 32;
/** Constructor
*
* @param context CTS context
**/
FunctionalTest11::FunctionalTest11(deqp::Context& context)
: TestCase(context, "globals_sampling_output_discard_function_calls", "Verify that global variables, texture "
"sampling, fragment output, fragment discard "
"and function calls work as expected")
{
}
/** Execute test
*
* @return tcu::TestNode::STOP
**/
tcu::TestNode::IterateResult FunctionalTest11::iterate()
{
static const GLchar* fragment_shader_code =
"#version 400 core\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"precision highp float;\n"
"\n"
"// Output\n"
"layout(location = 0) out vec4 out_color;\n"
"\n"
"// Global variables\n"
"vec4 success_color;\n"
"vec4 failure_color;\n"
"\n"
"// Samplers\n"
"uniform sampler2D sampler_1;\n"
"uniform sampler2D sampler_2;\n"
"\n"
"// Functions\n"
"bool are_same(in vec4 left, in vec4 right)\n"
"{\n"
" bvec4 result;\n"
"\n"
" result.x = (left.x == right.x);\n"
" result.y = (left.y == right.y);\n"
" result.z = (left.z == right.z);\n"
" result.w = (left.w == right.w);\n"
"\n"
" return all(result);\n"
"}\n"
"\n"
"bool are_different(in vec4 left, in vec4 right)\n"
"{\n"
" bvec4 result;\n"
"\n"
" result.x = (left.x != right.x);\n"
" result.y = (left.y != right.y);\n"
" result.z = (left.z != right.z);\n"
" result.w = (left.w != right.w);\n"
"\n"
" return any(result);\n"
"}\n"
"\n"
"// Subroutine types\n"
"subroutine void discard_fragment_type(void);\n"
"subroutine void set_global_colors_type(void);\n"
"subroutine vec4 sample_texture_type(in vec2);\n"
"subroutine bool comparison_type(in vec4 left, in vec4 right);\n"
"subroutine void test_type(void);\n"
"\n"
"// Subroutine definitions\n"
"// discard_fragment_type\n"
"subroutine(discard_fragment_type) void discard_yes(void)\n"
"{\n"
" discard;\n"
"}\n"
"\n"
"subroutine(discard_fragment_type) void discard_no(void)\n"
"{\n"
"}\n"
"\n"
"// set_global_colors_type\n"
"subroutine(set_global_colors_type) void red_pass_blue_fail(void)\n"
"{\n"
" success_color = vec4(1, 0, 0, 1);\n"
" failure_color = vec4(0, 0, 1, 1);\n"
"}\n"
"\n"
"subroutine(set_global_colors_type) void blue_pass_red_fail(void)\n"
"{\n"
" success_color = vec4(0, 0, 1, 1);\n"
" failure_color = vec4(1, 0, 0, 1);\n"
"}\n"
"\n"
"// sample_texture_type\n"
"subroutine(sample_texture_type) vec4 first_sampler(in vec2 coord)\n"
"{\n"
" return texture(sampler_1, coord);\n"
"}\n"
"\n"
"subroutine(sample_texture_type) vec4 second_sampler(in vec2 coord)\n"
"{\n"
" return texture(sampler_2, coord);\n"
"}\n"
"\n"
"// comparison_type\n"
"subroutine(comparison_type) bool check_equal(in vec4 left, in vec4 right)\n"
"{\n"
" return are_same(left, right);\n"
"}\n"
"\n"
"subroutine(comparison_type) bool check_not_equal(in vec4 left, in vec4 right)\n"
"{\n"
" return are_different(left, right);\n"
"}\n"
"\n"
"// Subroutine uniforms\n"
"subroutine uniform discard_fragment_type discard_fragment;\n"
"subroutine uniform set_global_colors_type set_global_colors;\n"
"subroutine uniform sample_texture_type sample_texture;\n"
"subroutine uniform comparison_type compare;\n"
"\n"
"// Subroutine definitions\n"
"// test_type\n"
"subroutine(test_type) void test_with_discard(void)\n"
"{\n"
" discard_fragment();"
"\n"
" out_color = failure_color;\n"
"\n"
" set_global_colors();\n"
"\n"
" vec4 sampled_color = sample_texture(gl_PointCoord);\n"
"\n"
" bool comparison_result = compare(success_color, sampled_color);\n"
"\n"
" if (true == comparison_result)\n"
" {\n"
" out_color = success_color;\n"
" }\n"
" else\n"
" {\n"
" out_color = failure_color;\n"
" }\n"
"}\n"
"\n"
"subroutine(test_type) void test_without_discard(void)\n"
"{\n"
" set_global_colors();\n"
"\n"
" vec4 sampled_color = sample_texture(gl_PointCoord);\n"
"\n"
" bool comparison_result = compare(success_color, sampled_color);\n"
"\n"
" if (true == comparison_result)\n"
" {\n"
" out_color = success_color;\n"
" }\n"
" else\n"
" {\n"
" out_color = failure_color;\n"
" }\n"
"}\n"
"\n"
"// Subroutine uniforms\n"
"subroutine uniform test_type test;\n"
"\n"
"void main()\n"
"{\n"
" // Set colors\n"
" success_color = vec4(0.5, 0.5, 0.5, 0.5);\n"
" failure_color = vec4(0.5, 0.5, 0.5, 0.5);\n"
"\n"
" test();\n"
"}\n"
"\n";
static const GLchar* geometry_shader_code = "#version 400 core\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"precision highp float;\n"
"\n"
"layout(points) in;\n"
"layout(triangle_strip, max_vertices = 4) out;\n"
"\n"
"void main()\n"
"{\n"
" gl_Position = vec4(-1, -1, 0, 1);\n"
" EmitVertex();\n"
" \n"
" gl_Position = vec4(-1, 1, 0, 1);\n"
" EmitVertex();\n"
" \n"
" gl_Position = vec4( 1, -1, 0, 1);\n"
" EmitVertex();\n"
" \n"
" gl_Position = vec4( 1, 1, 0, 1);\n"
" EmitVertex();\n"
" \n"
" EndPrimitive();\n"
"}\n"
"\n";
static const GLchar* vertex_shader_code = "#version 400 core\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"precision highp float;\n"
"\n"
"void main()\n"
"{\n"
"}\n"
"\n";
static const GLchar* subroutine_names[][2] = { { "discard_yes", "discard_no" },
{ "red_pass_blue_fail", "blue_pass_red_fail" },
{ "first_sampler", "second_sampler" },
{ "check_equal", "check_not_equal" },
{ "test_with_discard", "test_without_discard" } };
static const GLuint n_subroutine_types = sizeof(subroutine_names) / sizeof(subroutine_names[0]);
static const GLchar* subroutine_uniform_names[] = { "discard_fragment", "set_global_colors", "sample_texture",
"compare", "test" };
static const GLuint n_subroutine_uniform_names =
sizeof(subroutine_uniform_names) / sizeof(subroutine_uniform_names[0]);
static const GLchar* uniform_names[] = {
"sampler_1", "sampler_2",
};
static const GLuint n_uniform_names = sizeof(uniform_names) / sizeof(uniform_names[0]);
/* Colors */
static const GLubyte blue_color[4] = { 0, 0, 255, 255 };
static const GLubyte clean_color[4] = { 0, 0, 0, 0 };
static const GLubyte red_color[4] = { 255, 0, 0, 255 };
/* Configurations */
static const testConfiguration test_configurations[] = {
testConfiguration(
"Expect red color from 1st sampler", red_color, 1 /* discard_fragment : discard_no */,
0 /* set_global_colors : red_pass_blue_fail */, 0 /* sample_texture : first_sampler */,
0 /* compare : check_equal */, 0 /* test : test_with_discard */, 1 /* red */,
0 /* blue */),
testConfiguration(
"Test \"without discard\" option, expect no blue color from 2nd sampler", blue_color,
0 /* discard_fragment : discard_yes */, 1 /* set_global_colors : blue_pass_red_fail */,
1 /* sample_texture : second_sampler */, 1 /* compare : check_not_equal */,
1 /* test : test_without_discard */, 0 /* blue */, 1 /* red */),
testConfiguration("Fragment shoud be discarded", clean_color, 0 /* discard_fragment : discard_yes */,
0 /* set_global_colors : red_pass_blue_fail */,
0 /* sample_texture : first_sampler */,
0 /* compare : check_equal */,
0 /* test : test_with_discard */, 1 /* red */, 0 /* blue */),
testConfiguration(
"Expect blue color from 1st sampler", blue_color, 1 /* discard_fragment : discard_no */,
1 /* set_global_colors : blue_pass_red_fail */, 0 /* sample_texture : first_sampler */,
0 /* compare : check_equal */, 0 /* test : test_with_discard */,
0 /* blue */, 1 /* red */),
testConfiguration(
"Expect red color from 2nd sampler", red_color, 1 /* discard_fragment : discard_no */,
0 /* set_global_colors : red_pass_blue_fail */, 1 /* sample_texture : second_sampler */,
0 /* compare : check_equal */, 0 /* test : test_with_discard */,
0 /* blue */, 1 /* red */),
testConfiguration(
"Expect no blue color from 2nd sampler", blue_color, 1 /* discard_fragment : discard_no */,
1 /* set_global_colors : blue_pass_red_fail */, 1 /* sample_texture : second_sampler */,
1 /* compare : check_not_equal */, 0 /* test : test_with_discard */,
0 /* blue */, 1 /* red */),
};
static const GLuint n_test_cases = sizeof(test_configurations) / sizeof(test_configurations[0]);
/* Do not execute the test if GL_ARB_shader_subroutine is not supported */
if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_shader_subroutine"))
{
throw tcu::NotSupportedError("GL_ARB_shader_subroutine is not supported.");
}
/* GL objects */
Utils::texture blue_texture(m_context);
Utils::texture color_texture(m_context);
Utils::framebuffer framebuffer(m_context);
Utils::program program(m_context);
Utils::texture red_texture(m_context);
Utils::vertexArray vao(m_context);
/* Init GL objects */
program.build(0 /* cs */, fragment_shader_code /* fs */, geometry_shader_code /* gs */, 0 /* tcs */, 0 /* test */,
vertex_shader_code, 0 /* varying_names */, 0 /* n_varyings */);
program.use();
vao.generate();
vao.bind();
blue_texture.create(m_texture_width, m_texture_height, GL_RGBA8);
color_texture.create(m_texture_width, m_texture_height, GL_RGBA8);
red_texture.create(m_texture_width, m_texture_height, GL_RGBA8);
framebuffer.generate();
framebuffer.bind();
framebuffer.attachTexture(GL_COLOR_ATTACHMENT0, color_texture.m_id, m_texture_width, m_texture_height);
/* Get subroutine indices */
for (GLuint type = 0; type < n_subroutine_types; ++type)
{
m_subroutine_indices[type][0] = program.getSubroutineIndex(subroutine_names[type][0], GL_FRAGMENT_SHADER);
m_subroutine_indices[type][1] = program.getSubroutineIndex(subroutine_names[type][1], GL_FRAGMENT_SHADER);
}
/* Get subroutine uniform locations */
for (GLuint uniform = 0; uniform < n_subroutine_uniform_names; ++uniform)
{
m_subroutine_uniform_locations[uniform] =
program.getSubroutineUniformLocation(subroutine_uniform_names[uniform], GL_FRAGMENT_SHADER);
}
/* Get uniform locations */
for (GLuint i = 0; i < n_uniform_names; ++i)
{
m_uniform_locations[i] = program.getUniformLocation(uniform_names[i]);
}
/* Prepare textures */
fillTexture(blue_texture, blue_color);
fillTexture(color_texture, clean_color);
fillTexture(red_texture, red_color);
m_source_textures[0] = blue_texture.m_id;
m_source_textures[1] = red_texture.m_id;
framebuffer.clearColor(0.0f, 0.0f, 0.0f, 0.0f);
/* Test */
bool result = true;
for (GLuint i = 0; i < n_test_cases; ++i)
{
/* Clean output texture */
framebuffer.clear(GL_COLOR_BUFFER_BIT);
/* Execute test */
if (false == testDraw(test_configurations[i].m_routines, test_configurations[i].m_samplers,
test_configurations[i].m_expected_color, color_texture))
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "Error. Failure for configuration: " << test_configurations[i].m_description
<< tcu::TestLog::EndMessage;
result = false;
}
}
/* Set result */
if (true == result)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
/* Done */
return tcu::TestNode::STOP;
}
/** Fill texture with specified color
*
* @param texture Texture instance
* @param color Color
**/
void FunctionalTest11::fillTexture(Utils::texture& texture, const glw::GLubyte color[4]) const
{
std::vector<GLubyte> texture_data;
/* Prepare texture data */
texture_data.resize(m_texture_width * m_texture_height * 4);
for (GLuint y = 0; y < m_texture_height; ++y)
{
const GLuint line_offset = y * m_texture_width * 4;
for (GLuint x = 0; x < m_texture_width; ++x)
{
const GLuint point_offset = x * 4 + line_offset;
texture_data[point_offset + 0] = color[0]; /* red */
texture_data[point_offset + 1] = color[1]; /* green */
texture_data[point_offset + 2] = color[2]; /* blue */
texture_data[point_offset + 3] = color[3]; /* alpha */
}
}
texture.update(m_texture_width, m_texture_height, GL_RGBA, GL_UNSIGNED_BYTE, &texture_data[0]);
}
/** Execute draw call and verify results
*
* @param routine_configuration Configurations of routines to be used
* @param sampler_configuration Configuration of textures to be bound to samplers
* @param expected_color Expected color of result image
*
* @return true if result image is filled with expected color, false otherwise
**/
bool FunctionalTest11::testDraw(const glw::GLuint routine_configuration[5], const glw::GLuint sampler_configuration[2],
const glw::GLubyte expected_color[4], Utils::texture& color_texture) const
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
static const GLint n_samplers = 2;
static const GLint n_subroutine_uniforms = 5;
GLuint subroutine_indices[5];
/* Set samplers */
for (GLuint i = 0; i < n_samplers; ++i)
{
const GLuint location = m_uniform_locations[i];
const GLuint texture = m_source_textures[sampler_configuration[i]];
gl.activeTexture(GL_TEXTURE0 + i);
GLU_EXPECT_NO_ERROR(gl.getError(), "ActiveTexture");
gl.bindTexture(GL_TEXTURE_2D, texture);
GLU_EXPECT_NO_ERROR(gl.getError(), "BindTexture");
gl.uniform1i(location, i);
GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform1i");
}
gl.activeTexture(GL_TEXTURE0 + 0);
/* Set subroutine uniforms */
for (GLuint i = 0; i < n_subroutine_uniforms; ++i)
{
const GLuint location = m_subroutine_uniform_locations[i];
const GLuint routine = routine_configuration[i];
subroutine_indices[location] = m_subroutine_indices[i][routine];
}
gl.uniformSubroutinesuiv(GL_FRAGMENT_SHADER, 5, subroutine_indices);
GLU_EXPECT_NO_ERROR(gl.getError(), "UniformSubroutinesuiv");
/* Draw */
gl.drawArrays(GL_POINTS, 0 /* first */, 1 /* count */);
GLU_EXPECT_NO_ERROR(gl.getError(), "DrawArrays");
/* Capture result */
std::vector<GLubyte> captured_data;
captured_data.resize(m_texture_width * m_texture_height * 4);
color_texture.get(GL_RGBA, GL_UNSIGNED_BYTE, &captured_data[0]);
/* Verify result */
for (GLuint y = 0; y < m_texture_height; ++y)
{
const GLuint line_offset = y * m_texture_width * 4;
for (GLuint x = 0; x < m_texture_width; ++x)
{
const GLuint point_offset = x * 4 + line_offset;
bool is_as_expected = true;
is_as_expected = is_as_expected && (expected_color[0] == captured_data[point_offset + 0]); /* red */
is_as_expected = is_as_expected && (expected_color[1] == captured_data[point_offset + 1]); /* green */
is_as_expected = is_as_expected && (expected_color[2] == captured_data[point_offset + 2]); /* blue */
is_as_expected = is_as_expected && (expected_color[3] == captured_data[point_offset + 3]); /* alpha */
if (false == is_as_expected)
{
return false;
}
}
}
/* Done */
return true;
}
/* Constatns used by FunctionalTest12 */
const glw::GLuint FunctionalTest12::m_texture_height = 16;
const glw::GLuint FunctionalTest12::m_texture_width = 16;
/** Constructor
*
* @param context CTS context
**/
FunctionalTest12::FunctionalTest12(deqp::Context& context)
: TestCase(context, "ssbo_atomic_image_load_store",
"Verify that SSBO, atomic counters and image load store work as expected")
, m_left_image(0)
, m_right_image(0)
{
}
/** Execute test
*
* @return tcu::TestNode::STOP
**/
tcu::TestNode::IterateResult FunctionalTest12::iterate()
{
/* Do not execute the test if GL_ARB_shader_subroutine is not supported */
if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_shader_subroutine"))
{
throw tcu::NotSupportedError("GL_ARB_shader_subroutine is not supported.");
}
bool result = true;
/* Test atomic counters */
if (true == m_context.getContextInfo().isExtensionSupported("GL_ARB_shader_atomic_counters"))
{
if (false == testAtomic())
{
result = false;
}
}
/* Test shader storage buffer */
if (true == m_context.getContextInfo().isExtensionSupported("GL_ARB_shader_storage_buffer_object"))
{
if (false == testSSBO())
{
result = false;
}
}
/* Test image load store */
if (true == m_context.getContextInfo().isExtensionSupported("GL_ARB_shader_image_load_store"))
{
if (false == testImage())
{
result = false;
}
}
/* Set result */
if (true == result)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
/* Done */
return tcu::TestNode::STOP;
}
/** Fill texture with specified color
*
* @param texture Texture instance
* @param color Color
**/
void FunctionalTest12::fillTexture(Utils::texture& texture, const glw::GLuint color[4]) const
{
std::vector<GLuint> texture_data;
/* Prepare texture data */
texture_data.resize(m_texture_width * m_texture_height * 4);
for (GLuint y = 0; y < m_texture_height; ++y)
{
const GLuint line_offset = y * m_texture_width * 4;
for (GLuint x = 0; x < m_texture_width; ++x)
{
const GLuint point_offset = x * 4 + line_offset;
texture_data[point_offset + 0] = color[0]; /* red */
texture_data[point_offset + 1] = color[1]; /* green */
texture_data[point_offset + 2] = color[2]; /* blue */
texture_data[point_offset + 3] = color[3]; /* alpha */
}
}
texture.update(m_texture_width, m_texture_height, GL_RGBA_INTEGER, GL_UNSIGNED_INT, &texture_data[0]);
}
/** Test atomic counters
*
* @return true if test pass, false otherwise
**/
bool FunctionalTest12::testAtomic()
{
static const GLchar* fragment_shader_code = "#version 410 core\n"
"#extension GL_ARB_shader_atomic_counters : require\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"precision highp float;\n"
"\n"
"layout(location = 0) out uint out_color;\n"
"\n"
"layout(binding = 0, offset = 8) uniform atomic_uint one;\n"
"layout(binding = 0, offset = 4) uniform atomic_uint two;\n"
"layout(binding = 0, offset = 0) uniform atomic_uint three;\n"
"\n"
"subroutine void atomic_routine(void)\n;"
"\n"
"subroutine(atomic_routine) void increment_two(void)\n"
"{\n"
" out_color = atomicCounterIncrement(two);\n"
"}\n"
"\n"
"subroutine(atomic_routine) void decrement_three(void)\n"
"{\n"
" out_color = atomicCounterDecrement(three);\n"
"}\n"
"\n"
"subroutine(atomic_routine) void read_one(void)\n"
"{\n"
" out_color = atomicCounter(one);\n"
"}\n"
"\n"
"subroutine uniform atomic_routine routine;\n"
"\n"
"void main()\n"
"{\n"
" routine();\n"
"}\n"
"\n";
static const GLchar* geometry_shader_code = "#version 400 core\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"precision highp float;\n"
"\n"
"layout(points) in;\n"
"layout(triangle_strip, max_vertices = 4) out;\n"
"\n"
"void main()\n"
"{\n"
" gl_Position = vec4(-1, -1, 0, 1);\n"
" EmitVertex();\n"
" \n"
" gl_Position = vec4(-1, 1, 0, 1);\n"
" EmitVertex();\n"
" \n"
" gl_Position = vec4( 1, -1, 0, 1);\n"
" EmitVertex();\n"
" \n"
" gl_Position = vec4( 1, 1, 0, 1);\n"
" EmitVertex();\n"
" \n"
" EndPrimitive();\n"
"}\n"
"\n";
static const GLchar* vertex_shader_code = "#version 400 core\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"precision highp float;\n"
"\n"
"void main()\n"
"{\n"
"}\n"
"\n";
static const GLchar* subroutine_names[] = { "increment_two", "decrement_three", "read_one" };
/* Test data */
static const glw::GLuint atomic_buffer_data[] = { m_texture_width * m_texture_height,
m_texture_width * m_texture_height,
m_texture_width * m_texture_height };
static const glw::GLuint expected_incremented_two[] = { atomic_buffer_data[0], 2 * atomic_buffer_data[1],
atomic_buffer_data[2] };
static const glw::GLuint expected_decremented_three[] = { 0, expected_incremented_two[1],
expected_incremented_two[2] };
static const glw::GLuint expected_read_one[] = { expected_decremented_three[0], expected_decremented_three[1],
expected_decremented_three[2] };
/* GL objects */
Utils::buffer atomic_buffer(m_context);
Utils::texture color_texture(m_context);
Utils::framebuffer framebuffer(m_context);
Utils::program program(m_context);
Utils::vertexArray vao(m_context);
/* Init GL objects */
program.build(0 /* cs */, fragment_shader_code /* fs */, geometry_shader_code /* gs */, 0 /* tcs */, 0 /* test */,
vertex_shader_code, 0 /* varying_names */, 0 /* n_varyings */);
program.use();
vao.generate();
vao.bind();
color_texture.create(m_texture_width, m_texture_height, GL_R32UI);
atomic_buffer.generate();
atomic_buffer.update(GL_ATOMIC_COUNTER_BUFFER, sizeof(atomic_buffer_data), (GLvoid*)atomic_buffer_data,
GL_STATIC_DRAW);
atomic_buffer.bindRange(GL_ATOMIC_COUNTER_BUFFER, 0 /* index */, 0 /* offset */, sizeof(atomic_buffer_data));
framebuffer.generate();
framebuffer.bind();
framebuffer.attachTexture(GL_COLOR_ATTACHMENT0, color_texture.m_id, m_texture_width, m_texture_height);
framebuffer.clearColor(0.0f, 0.0f, 0.0f, 0.0f);
framebuffer.clear(GL_COLOR_BUFFER_BIT);
/* Subroutine indices */
GLuint increment_two = program.getSubroutineIndex(subroutine_names[0], GL_FRAGMENT_SHADER);
GLuint decrement_three = program.getSubroutineIndex(subroutine_names[1], GL_FRAGMENT_SHADER);
GLuint read_one = program.getSubroutineIndex(subroutine_names[2], GL_FRAGMENT_SHADER);
/* Test */
bool result = true;
if (false == testAtomicDraw(increment_two, expected_incremented_two))
{
result = false;
}
if (false == testAtomicDraw(decrement_three, expected_decremented_three))
{
result = false;
}
if (false == testAtomicDraw(read_one, expected_read_one))
{
result = false;
}
/* Done */
return result;
}
/** Execture draw call and verify results
*
* @param subroutine_index Index of subroutine that shall be used during draw call
* @param expected_results Expected results
*
* @return true if results are as expected, false otherwise
**/
bool FunctionalTest12::testAtomicDraw(GLuint subroutine_index, const GLuint expected_results[3]) const
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Set subroutine uniforms */
gl.uniformSubroutinesuiv(GL_FRAGMENT_SHADER, 1, &subroutine_index);
GLU_EXPECT_NO_ERROR(gl.getError(), "UniformSubroutinesuiv");
/* Draw */
gl.drawArrays(GL_POINTS, 0 /* first */, 1 /* count */);
GLU_EXPECT_NO_ERROR(gl.getError(), "DrawArrays");
/* Capture results */
GLuint* atomic_results = (GLuint*)gl.mapBuffer(GL_ATOMIC_COUNTER_BUFFER, GL_READ_ONLY);
GLU_EXPECT_NO_ERROR(gl.getError(), "MapBuffer");
/* Verify */
bool result = (0 == memcmp(expected_results, atomic_results, 3 * sizeof(GLuint)));
if (false == result)
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "Error. Invalid result. "
<< "Result: [ " << atomic_results[0] << ", " << atomic_results[1] << ", " << atomic_results[2] << " ] "
<< "Expected: [ " << expected_results[0] << ", " << expected_results[1] << ", " << expected_results[2]
<< " ]" << tcu::TestLog::EndMessage;
}
/* Unmap buffer */
gl.unmapBuffer(GL_ATOMIC_COUNTER_BUFFER);
GLU_EXPECT_NO_ERROR(gl.getError(), "UnmapBuffer");
/* Done */
return result;
}
/** Test image load store
*
* @return true if test pass, false otherwise
**/
bool FunctionalTest12::testImage()
{
static const GLchar* fragment_shader_code =
"#version 400 core\n"
"#extension GL_ARB_shader_image_load_store : require\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"precision highp float;\n"
"\n"
"layout(location = 0) out uvec4 out_color;\n"
"\n"
"layout(rgba32ui) uniform uimage2D left_image;\n"
"layout(rgba32ui) uniform uimage2D right_image;\n"
"\n"
"subroutine void image_routine(void);\n"
"\n"
"subroutine(image_routine) void left_to_right(void)\n"
"{\n"
" out_color = imageLoad (left_image, ivec2(gl_FragCoord.xy));\n"
" imageStore(right_image, ivec2(gl_FragCoord.xy), out_color);\n"
"}\n"
"\n"
"subroutine(image_routine) void right_to_left(void)\n"
"{\n"
" out_color = imageLoad (right_image, ivec2(gl_FragCoord.xy));\n"
" imageStore(left_image, ivec2(gl_FragCoord.xy), out_color);\n"
"}\n"
"\n"
"subroutine uniform image_routine routine;\n"
"\n"
"void main()\n"
"{\n"
" routine();\n"
"}\n"
"\n";
static const GLchar* geometry_shader_code = "#version 400 core\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"precision highp float;\n"
"\n"
"layout(points) in;\n"
"layout(triangle_strip, max_vertices = 4) out;\n"
"\n"
"void main()\n"
"{\n"
" gl_Position = vec4(-1, -1, 0, 1);\n"
" EmitVertex();\n"
" \n"
" gl_Position = vec4(-1, 1, 0, 1);\n"
" EmitVertex();\n"
" \n"
" gl_Position = vec4( 1, -1, 0, 1);\n"
" EmitVertex();\n"
" \n"
" gl_Position = vec4( 1, 1, 0, 1);\n"
" EmitVertex();\n"
" \n"
" EndPrimitive();\n"
"}\n"
"\n";
static const GLchar* vertex_shader_code = "#version 400 core\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"precision highp float;\n"
"\n"
"void main()\n"
"{\n"
"}\n"
"\n";
static const GLchar* subroutine_names[] = { "left_to_right", "right_to_left" };
static const GLchar* uniform_names[] = { "left_image", "right_image" };
/* Test data */
static const GLuint blue_color[4] = { 0, 0, 255, 255 };
static const GLuint clean_color[4] = { 16, 32, 64, 128 };
static const GLuint red_color[4] = { 255, 0, 0, 255 };
/* GL objects */
Utils::texture blue_texture(m_context);
Utils::texture destination_texture(m_context);
Utils::texture color_texture(m_context);
Utils::framebuffer framebuffer(m_context);
Utils::program program(m_context);
Utils::texture red_texture(m_context);
Utils::vertexArray vao(m_context);
/* Init GL objects */
program.build(0 /* cs */, fragment_shader_code /* fs */, geometry_shader_code /* gs */, 0 /* tcs */, 0 /* test */,
vertex_shader_code, 0 /* varying_names */, 0 /* n_varyings */);
program.use();
vao.generate();
vao.bind();
blue_texture.create(m_texture_width, m_texture_height, GL_RGBA32UI);
destination_texture.create(m_texture_width, m_texture_height, GL_RGBA32UI);
color_texture.create(m_texture_width, m_texture_height, GL_RGBA32UI);
red_texture.create(m_texture_width, m_texture_height, GL_RGBA32UI);
fillTexture(blue_texture, blue_color);
fillTexture(destination_texture, clean_color);
fillTexture(red_texture, red_color);
framebuffer.generate();
framebuffer.bind();
framebuffer.attachTexture(GL_COLOR_ATTACHMENT0, color_texture.m_id, m_texture_width, m_texture_height);
framebuffer.clearColor(0.0f, 0.0f, 0.0f, 0.0f);
framebuffer.clear(GL_COLOR_BUFFER_BIT);
/* Subroutine indices */
GLuint left_to_right = program.getSubroutineIndex(subroutine_names[0], GL_FRAGMENT_SHADER);
GLuint right_to_left = program.getSubroutineIndex(subroutine_names[1], GL_FRAGMENT_SHADER);
/* Uniform locations */
m_left_image = program.getUniformLocation(uniform_names[0]);
m_right_image = program.getUniformLocation(uniform_names[1]);
/* Test */
bool result = true;
if (false == testImageDraw(left_to_right, blue_texture, destination_texture, blue_color, blue_color))
{
result = false;
}
if (false == testImageDraw(left_to_right, red_texture, destination_texture, red_color, red_color))
{
result = false;
}
if (false == testImageDraw(right_to_left, destination_texture, blue_texture, blue_color, blue_color))
{
result = false;
}
if (false == testImageDraw(right_to_left, destination_texture, red_texture, red_color, red_color))
{
result = false;
}
if (false == testImageDraw(left_to_right, blue_texture, red_texture, blue_color, blue_color))
{
result = false;
}
/* Done */
return result;
}
/** Execute draw call and verifies results
*
* @param subroutine_index Index of subroutine that shall be used during draw call
* @param left "Left" texture
* @param right "Right" texture
* @param expected_left_color Expected color of "left" texture
* @param expected_right_color Expected color of "right" texture
*
* @return true if verification result is positive, false otherwise
**/
bool FunctionalTest12::testImageDraw(GLuint subroutine_index, Utils::texture& left, Utils::texture& right,
const GLuint expected_left_color[4], const GLuint expected_right_color[4]) const
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Set subroutine uniforms */
gl.uniformSubroutinesuiv(GL_FRAGMENT_SHADER, 1, &subroutine_index);
GLU_EXPECT_NO_ERROR(gl.getError(), "UniformSubroutinesuiv");
/* Set up image units */
gl.uniform1i(m_left_image, 0);
GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform1i");
gl.uniform1i(m_right_image, 1);
GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform1i");
gl.bindImageTexture(0, left.m_id, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32UI);
GLU_EXPECT_NO_ERROR(gl.getError(), "BindImageTexture");
gl.bindImageTexture(1, right.m_id, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32UI);
GLU_EXPECT_NO_ERROR(gl.getError(), "BindImageTexture");
/* Draw */
gl.drawArrays(GL_POINTS, 0 /* first */, 1 /* count */);
GLU_EXPECT_NO_ERROR(gl.getError(), "DrawArrays");
/* Verify results */
bool result = true;
if (false == verifyTexture(left, expected_left_color))
{
m_context.getTestContext().getLog() << tcu::TestLog::Message
<< "Error. Invalid result. Left texture is filled with wrong color."
<< tcu::TestLog::EndMessage;
result = false;
}
if (false == verifyTexture(right, expected_right_color))
{
m_context.getTestContext().getLog() << tcu::TestLog::Message
<< "Error. Invalid result. Right texture is filled with wrong color."
<< tcu::TestLog::EndMessage;
result = false;
}
/* Done */
return result;
}
/** Test shader storage buffer
*
* @return true if test pass, false otherwise
**/
bool FunctionalTest12::testSSBO()
{
static const GLchar* fragment_shader_code = "#version 400 core\n"
"#extension GL_ARB_shader_storage_buffer_object : require\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"precision highp float;\n"
"\n"
"layout(location = 0) out uvec4 out_color;\n"
"\n"
"layout(std140, binding = 0) buffer Buffer\n"
"{\n"
" uvec4 entry;\n"
"};\n"
"\n"
"subroutine void ssbo_routine(void)\n;"
"\n"
"subroutine(ssbo_routine) void increment(void)\n"
"{\n"
" out_color.x = atomicAdd(entry.x, 1);\n"
" out_color.y = atomicAdd(entry.y, 1);\n"
" out_color.z = atomicAdd(entry.z, 1);\n"
" out_color.w = atomicAdd(entry.w, 1);\n"
"}\n"
"\n"
"subroutine(ssbo_routine) void decrement(void)\n"
"{\n"
" out_color.x = atomicAdd(entry.x, -1);\n"
" out_color.y = atomicAdd(entry.y, -1);\n"
" out_color.z = atomicAdd(entry.z, -1);\n"
" out_color.w = atomicAdd(entry.w, -1);\n"
"}\n"
"\n"
"subroutine uniform ssbo_routine routine;\n"
"\n"
"void main()\n"
"{\n"
" routine();\n"
"}\n"
"\n";
static const GLchar* geometry_shader_code = "#version 400 core\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"precision highp float;\n"
"\n"
"layout(points) in;\n"
"layout(triangle_strip, max_vertices = 4) out;\n"
"\n"
"void main()\n"
"{\n"
" gl_Position = vec4(-1, -1, 0, 1);\n"
" EmitVertex();\n"
" \n"
" gl_Position = vec4(-1, 1, 0, 1);\n"
" EmitVertex();\n"
" \n"
" gl_Position = vec4( 1, -1, 0, 1);\n"
" EmitVertex();\n"
" \n"
" gl_Position = vec4( 1, 1, 0, 1);\n"
" EmitVertex();\n"
" \n"
" EndPrimitive();\n"
"}\n"
"\n";
static const GLchar* vertex_shader_code = "#version 400 core\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"precision highp float;\n"
"\n"
"void main()\n"
"{\n"
"}\n"
"\n";
static const GLchar* subroutine_names[] = { "increment", "decrement" };
/* Test data */
static const glw::GLuint buffer_data[] = { m_texture_width * m_texture_height + 1,
m_texture_width * m_texture_height + 2,
m_texture_width * m_texture_height + 3,
m_texture_width * m_texture_height + 4 };
static const glw::GLuint expected_incremented[] = { m_texture_width * m_texture_height + buffer_data[0],
m_texture_width * m_texture_height + buffer_data[1],
m_texture_width * m_texture_height + buffer_data[2],
m_texture_width * m_texture_height + buffer_data[3] };
static const glw::GLuint expected_decremented[] = { buffer_data[0], buffer_data[1], buffer_data[2],
buffer_data[3] };
/* GL objects */
Utils::buffer buffer(m_context);
Utils::texture color_texture(m_context);
Utils::framebuffer framebuffer(m_context);
Utils::program program(m_context);
Utils::vertexArray vao(m_context);
/* Init GL objects */
program.build(0 /* cs */, fragment_shader_code /* fs */, geometry_shader_code /* gs */, 0 /* tcs */, 0 /* test */,
vertex_shader_code, 0 /* varying_names */, 0 /* n_varyings */);
program.use();
vao.generate();
vao.bind();
color_texture.create(m_texture_width, m_texture_height, GL_RGBA32UI);
buffer.generate();
buffer.update(GL_SHADER_STORAGE_BUFFER, sizeof(buffer_data), (GLvoid*)buffer_data, GL_STATIC_DRAW);
buffer.bindRange(GL_SHADER_STORAGE_BUFFER, 0 /* index */, 0 /* offset */, sizeof(buffer_data));
framebuffer.generate();
framebuffer.bind();
framebuffer.attachTexture(GL_COLOR_ATTACHMENT0, color_texture.m_id, m_texture_width, m_texture_height);
framebuffer.clearColor(0.0f, 0.0f, 0.0f, 0.0f);
framebuffer.clear(GL_COLOR_BUFFER_BIT);
/* Subroutine indices */
GLuint increment = program.getSubroutineIndex(subroutine_names[0], GL_FRAGMENT_SHADER);
GLuint decrement = program.getSubroutineIndex(subroutine_names[1], GL_FRAGMENT_SHADER);
/* Test */
bool result = true;
if (false == testSSBODraw(increment, expected_incremented))
{
result = false;
}
if (false == testSSBODraw(decrement, expected_decremented))
{
result = false;
}
/* Done */
return result;
}
/** Execute draw call and verify results
*
* @param subroutine_index Index of subroutine that shall be used by draw call
* @param expected_results Expected results
*
*
**/
bool FunctionalTest12::testSSBODraw(GLuint subroutine_index, const GLuint expected_results[4]) const
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Set subroutine uniforms */
gl.uniformSubroutinesuiv(GL_FRAGMENT_SHADER, 1, &subroutine_index);
GLU_EXPECT_NO_ERROR(gl.getError(), "UniformSubroutinesuiv");
/* Draw */
gl.drawArrays(GL_POINTS, 0 /* first */, 1 /* count */);
GLU_EXPECT_NO_ERROR(gl.getError(), "DrawArrays");
/* Capture results */
GLuint* ssbo_results = (GLuint*)gl.mapBuffer(GL_SHADER_STORAGE_BUFFER, GL_READ_ONLY);
GLU_EXPECT_NO_ERROR(gl.getError(), "MapBuffer");
/* Verify */
bool result = (0 == memcmp(expected_results, ssbo_results, 4 * sizeof(GLuint)));
if (false == result)
{
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Error. Invalid result. "
<< "Result: [ " << ssbo_results[0] << ", " << ssbo_results[1] << ", "
<< ssbo_results[2] << ", " << ssbo_results[3] << " ] "
<< "Expected: [ " << expected_results[0] << ", " << expected_results[1]
<< ", " << expected_results[2] << ", " << expected_results[3] << " ]"
<< tcu::TestLog::EndMessage;
}
/* Unmap buffer */
gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER);
GLU_EXPECT_NO_ERROR(gl.getError(), "UnmapBuffer");
/* Done */
return result;
}
/** Check if texture is filled with expected color
*
* @param texture Texture instance
* @param expected_color Expected color
*
* @return true if texture is filled with specified color, false otherwise
**/
bool FunctionalTest12::verifyTexture(Utils::texture& texture, const GLuint expected_color[4]) const
{
std::vector<GLuint> results;
results.resize(m_texture_width * m_texture_height * 4);
texture.get(GL_RGBA_INTEGER, GL_UNSIGNED_INT, &results[0]);
for (GLuint y = 0; y < m_texture_height; ++y)
{
const GLuint line_offset = y * m_texture_width * 4;
for (GLuint x = 0; x < m_texture_width; ++x)
{
const GLuint point_offset = line_offset + x * 4;
bool result = true;
result = result && (results[point_offset + 0] == expected_color[0]);
result = result && (results[point_offset + 1] == expected_color[1]);
result = result && (results[point_offset + 2] == expected_color[2]);
result = result && (results[point_offset + 3] == expected_color[3]);
if (false == result)
{
return false;
}
}
}
return true;
}
/** Constructor.
*
* @param context Rendering context.
*
**/
FunctionalTest13::FunctionalTest13(deqp::Context& context)
: TestCase(context, "subroutines_with_separate_shader_objects",
"Verifies that subroutines work correctly when used in separate "
"shader objects")
, m_fbo_id(0)
, m_pipeline_id(0)
, m_read_buffer(DE_NULL)
, m_to_height(4)
, m_to_id(0)
, m_to_width(4)
, m_vao_id(0)
, m_has_test_passed(true)
{
memset(m_fs_po_ids, 0, sizeof(m_fs_po_ids));
memset(m_gs_po_ids, 0, sizeof(m_gs_po_ids));
memset(m_tc_po_ids, 0, sizeof(m_tc_po_ids));
memset(m_te_po_ids, 0, sizeof(m_te_po_ids));
memset(m_vs_po_ids, 0, sizeof(m_vs_po_ids));
}
/** Deinitializes all GL objects that may have been created during test
* execution, as well as releases all process-side buffers that may have
* been allocated during the process.
* The function also restores default GL state configuration.
**/
void FunctionalTest13::deinit()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
if (m_fbo_id != 0)
{
gl.deleteFramebuffers(1, &m_fbo_id);
m_fbo_id = 0;
}
if (m_pipeline_id != 0)
{
gl.deleteProgramPipelines(1, &m_pipeline_id);
m_pipeline_id = 0;
}
if (m_read_buffer != DE_NULL)
{
delete[] m_read_buffer;
m_read_buffer = DE_NULL;
}
for (unsigned int n_id = 0; n_id < 2 /* po id variants */; ++n_id)
{
if (m_fs_po_ids[n_id] != 0)
{
gl.deleteProgram(m_fs_po_ids[n_id]);
m_fs_po_ids[n_id] = 0;
}
if (m_gs_po_ids[n_id] != 0)
{
gl.deleteProgram(m_gs_po_ids[n_id]);
m_gs_po_ids[n_id] = 0;
}
if (m_tc_po_ids[n_id] != 0)
{
gl.deleteProgram(m_tc_po_ids[n_id]);
m_tc_po_ids[n_id] = 0;
}
if (m_te_po_ids[n_id] != 0)
{
gl.deleteProgram(m_te_po_ids[n_id]);
m_te_po_ids[n_id] = 0;
}
if (m_vs_po_ids[n_id] != 0)
{
gl.deleteProgram(m_vs_po_ids[n_id]);
m_vs_po_ids[n_id] = 0;
}
} /* for (both shader program object variants) */
if (m_to_id != 0)
{
gl.deleteTextures(1, &m_to_id);
m_to_id = 0;
}
if (m_vao_id != 0)
{
gl.deleteVertexArrays(1, &m_vao_id);
m_vao_id = 0;
}
/* Restore default GL_PATCH_VERTICES setting value */
gl.patchParameteri(GL_PATCH_VERTICES, 3);
/* Restore default GL_PACK_ALIGNMENT setting value */
gl.pixelStorei(GL_PACK_ALIGNMENT, 4);
}
/** Retrieves body of a fragment shader that should be used for the test.
* The subroutine implementations are slightly changed, depending on the
* index of the shader, as specified by the caller.
*
* @param n_id Index of the shader.
*
* @return Requested string.
**/
std::string FunctionalTest13::getFragmentShaderBody(unsigned int n_id)
{
std::stringstream result_sstream;
/* Pre-amble */
result_sstream << "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
/* Sub-routine */
"subroutine void SubroutineFSType(inout vec4 result);\n"
"\n"
"subroutine(SubroutineFSType) void SubroutineFS1(inout vec4 result)\n"
"{\n"
" result += vec4("
<< float(n_id + 1) / 10.0f << ", " << float(n_id + 2) / 10.0f << ", " << float(n_id + 3) / 10.0f
<< ", " << float(n_id + 4) / 10.0f
<< ");\n"
"}\n"
"subroutine(SubroutineFSType) void SubroutineFS2(inout vec4 result)\n"
"{\n"
" result += vec4("
<< float(n_id + 1) / 20.0f << ", " << float(n_id + 2) / 20.0f << ", " << float(n_id + 3) / 20.0f
<< ", " << float(n_id + 4) / 20.0f << ");\n"
"}\n"
"\n"
"subroutine uniform SubroutineFSType function;\n"
"\n"
/* Input block */
"in GS_DATA\n"
"{\n"
" vec4 data;\n"
"} in_gs;\n"
"\n"
"out vec4 result;\n"
/* main() declaration */
"void main()\n"
"{\n"
" vec4 data = in_gs.data;\n"
" function(data);\n"
"\n"
" result = data;\n"
"}\n";
return result_sstream.str();
}
/** Retrieves body of a geometry shader that should be used for the test.
* The subroutine implementations are slightly changed, depending on the
* index of the shader, as specified by the caller.
*
* @param n_id Index of the shader.
*
* @return Requested string.
**/
std::string FunctionalTest13::getGeometryShaderBody(unsigned int n_id)
{
std::stringstream result_sstream;
/* Pre-amble */
result_sstream << "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"layout(points) in;\n"
"layout(triangle_strip, max_vertices = 4) out;\n"
/* Sub-routine */
"subroutine void SubroutineGSType(inout vec4 result);\n"
"\n"
"subroutine(SubroutineGSType) void SubroutineGS1(inout vec4 result)\n"
"{\n"
" result += vec4(0, 0, 0, "
<< float(n_id + 1) * 0.425f << ");\n"
"}\n"
"subroutine(SubroutineGSType) void SubroutineGS2(inout vec4 result)\n"
"{\n"
" result += vec4(0, 0, 0, "
<< float(n_id + 1) * 0.0425f << ");\n"
"}\n"
"\n"
"subroutine uniform SubroutineGSType function;\n"
"\n"
/* Input block */
"in TE_DATA\n"
"{\n"
" vec4 data;\n"
"} in_te[];\n"
"\n"
/* Output block */
"out GS_DATA\n"
"{\n"
" vec4 data;\n"
"} out_gs;\n"
"\n"
"in gl_PerVertex { vec4 gl_Position; } gl_in[];\n"
"out gl_PerVertex { vec4 gl_Position; };\n"
/* main() declaration */
"void main()\n"
"{\n"
" vec4 data = in_te[0].data;\n"
"\n"
" function(data);\n"
"\n"
" gl_Position = vec4(1, -1, 0, 1);\n"
" out_gs.data = data;\n"
" EmitVertex();\n"
"\n"
" gl_Position = vec4(-1, -1, 0, 1);\n"
" out_gs.data = data;\n"
" EmitVertex();\n"
"\n"
" gl_Position = vec4(1, 1, 0, 1);\n"
" out_gs.data = data;\n"
" EmitVertex();\n"
"\n"
" gl_Position = vec4(-1, 1, 0, 1);\n"
" out_gs.data = data;\n"
" EmitVertex();\n"
" EndPrimitive();\n"
"}\n";
return result_sstream.str();
}
/** Retrieves body of a tessellation control shader that should be used for the test.
* The subroutine implementations are slightly changed, depending on the
* index of the shader, as specified by the caller.
*
* @param n_id Index of the shader.
*
* @return Requested string.
**/
std::string FunctionalTest13::getTessellationControlShaderBody(unsigned int n_id)
{
std::stringstream result_sstream;
/* Pre-amble */
result_sstream << "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"layout(vertices = 4) out;\n"
/* Sub-routine */
"subroutine void SubroutineTCType(inout vec4 result);\n"
"\n"
"subroutine(SubroutineTCType) void SubroutineTC1(inout vec4 result)\n"
"{\n"
" result += vec4(0, "
<< float(n_id + 1) * 0.25f << ", 0, 0);\n"
"}\n"
"subroutine(SubroutineTCType) void SubroutineTC2(inout vec4 result)\n"
"{\n"
" result += vec4(0, "
<< float(n_id + 1) * 0.025f
<< ", 0, 0);\n"
"}\n"
"\n"
"subroutine uniform SubroutineTCType function;\n"
"\n"
/* Input block */
"in VS_DATA\n"
"{\n"
" vec4 data;\n"
"} in_vs[];\n"
"\n"
/* Output block */
"out TC_DATA\n"
"{\n"
" vec4 data;\n"
"} out_tc[];\n"
"\n"
"in gl_PerVertex { vec4 gl_Position; } gl_in[];\n"
"out gl_PerVertex { vec4 gl_Position; } gl_out[];\n"
/* main() declaration */
"void main()\n"
"{\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"
" gl_out[gl_InvocationID].gl_Position = gl_in[0].gl_Position;\n"
" out_tc[gl_InvocationID].data = in_vs[0].data;\n"
"\n"
" function(out_tc[gl_InvocationID].data);\n"
"}\n";
return result_sstream.str();
}
/** Retrieves body of a tessellation evaluation shader that should be used for the test.
* The subroutine implementations are slightly changed, depending on the
* index of the shader, as specified by the caller.
*
* @param n_id Index of the shader.
*
* @return Requested string.
**/
std::string FunctionalTest13::getTessellationEvaluationShaderBody(unsigned int n_id)
{
std::stringstream result_sstream;
/* Pre-amble */
result_sstream << "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"layout(quads, point_mode) in;\n"
/* Sub-routine */
"subroutine void SubroutineTEType(inout vec4 result);\n"
"\n"
"subroutine(SubroutineTEType) void SubroutineTE1(inout vec4 result)\n"
"{\n"
" result += vec4(0, 0, "
<< float(n_id + 1) * 0.325f << ", 0);\n"
"}\n"
"subroutine(SubroutineTEType) void SubroutineTE2(inout vec4 result)\n"
"{\n"
" result += vec4(0, 0, "
<< float(n_id + 1) * 0.0325f << ", 0);\n"
"}\n"
"\n"
"subroutine uniform SubroutineTEType function;\n"
"\n"
/* Input block */
"in TC_DATA\n"
"{\n"
" vec4 data;\n"
"} in_tc[];\n"
"\n"
/* Output block */
"out TE_DATA\n"
"{\n"
" vec4 data;\n"
"} out_te;\n"
"\n"
"in gl_PerVertex { vec4 gl_Position; } gl_in[];\n"
"out gl_PerVertex { vec4 gl_Position; };\n"
/* main() declaration */
"void main()\n"
"{\n"
" gl_Position = gl_in[0].gl_Position;\n"
" out_te.data = in_tc[0].data;\n"
"\n"
" function(out_te.data);\n"
"}\n";
return result_sstream.str();
}
/** Retrieves body of a vertex shader that should be used for the test.
* The subroutine implementations are slightly changed, depending on the
* index of the shader, as specified by the caller.
*
* @param n_id Index of the shader.
*
* @return Requested string.
**/
std::string FunctionalTest13::getVertexShaderBody(unsigned int n_id)
{
std::stringstream result_sstream;
/* Pre-amble */
result_sstream << "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"#extension GL_ARB_separate_shader_objects: require\n"
"\n"
/* Sub-routine */
"subroutine void SubroutineVSType(inout vec4 result);\n"
"\n"
"subroutine(SubroutineVSType) void SubroutineVS1(inout vec4 result)\n"
"{\n"
" result += vec4("
<< float(n_id + 1) * 0.125f << ", 0, 0, 0);\n"
"}\n"
"subroutine(SubroutineVSType) void SubroutineVS2(inout vec4 result)\n"
"{\n"
" result += vec4("
<< float(n_id + 1) * 0.0125f << ", 0, 0, 0);\n"
"}\n"
"\n"
"subroutine uniform SubroutineVSType function;\n"
"\n"
/* Output block */
"out VS_DATA\n"
"{\n"
" vec4 data;\n"
"} out_vs;\n"
"\n"
"out gl_PerVertex { vec4 gl_Position; };\n"
/* main() declaration */
"void main()\n"
"{\n"
" gl_Position = vec4(0, 0, 0, 1);\n"
" out_vs.data = vec4(0);\n"
"\n"
" function(out_vs.data);\n"
"\n"
"}\n";
return result_sstream.str();
}
/** Initializes all GL objects required to run the test. Also modifies a few
* GL states in order for the test to run correctly.
**/
void FunctionalTest13::initTest()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Set up viewport */
gl.viewport(0 /* x */, 0 /* y */, m_to_width, m_to_height);
GLU_EXPECT_NO_ERROR(gl.getError(), "glViewport() call failed.");
/* Make sure no program is used */
gl.useProgram(0);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() call failed.");
/* Generate a pipeline object */
gl.genProgramPipelines(1, &m_pipeline_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenProgramPipelines() call failed.");
gl.bindProgramPipeline(m_pipeline_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindProgramPipeline() call failed.");
/* Initialize all shader programs */
for (unsigned int n_id = 0; n_id < 2 /* variants for each shader type */; ++n_id)
{
std::string fs_body = getFragmentShaderBody(n_id);
const char* fs_body_raw_ptr = fs_body.c_str();
std::string gs_body = getGeometryShaderBody(n_id);
const char* gs_body_raw_ptr = gs_body.c_str();
std::string tc_body = getTessellationControlShaderBody(n_id);
const char* tc_body_raw_ptr = tc_body.c_str();
std::string te_body = getTessellationEvaluationShaderBody(n_id);
const char* te_body_raw_ptr = te_body.c_str();
std::string vs_body = getVertexShaderBody(n_id);
const char* vs_body_raw_ptr = vs_body.c_str();
m_fs_po_ids[n_id] = gl.createShaderProgramv(GL_FRAGMENT_SHADER, 1 /* count */, &fs_body_raw_ptr);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShaderProgramv() call failed.");
m_gs_po_ids[n_id] = gl.createShaderProgramv(GL_GEOMETRY_SHADER, 1 /* count */, &gs_body_raw_ptr);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShaderProgramv() call failed.");
m_tc_po_ids[n_id] = gl.createShaderProgramv(GL_TESS_CONTROL_SHADER, 1 /* count */, &tc_body_raw_ptr);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShaderProgramv() call failed.");
m_te_po_ids[n_id] = gl.createShaderProgramv(GL_TESS_EVALUATION_SHADER, 1 /* count */, &te_body_raw_ptr);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShaderProgramv() call failed.");
m_vs_po_ids[n_id] = gl.createShaderProgramv(GL_VERTEX_SHADER, 1 /* count */, &vs_body_raw_ptr);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShaderProgramv() call failed.");
/* Verify that all shader program objects have been linked successfully */
const glw::GLuint po_ids[] = {
m_fs_po_ids[n_id], m_gs_po_ids[n_id], m_tc_po_ids[n_id], m_te_po_ids[n_id], m_vs_po_ids[n_id],
};
const unsigned int n_po_ids = sizeof(po_ids) / sizeof(po_ids[0]);
for (unsigned int n_po_id = 0; n_po_id < n_po_ids; ++n_po_id)
{
glw::GLint link_status = GL_FALSE;
glw::GLuint po_id = po_ids[n_po_id];
gl.getProgramiv(po_id, GL_LINK_STATUS, &link_status);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv() call failed.");
if (link_status != GL_TRUE)
{
TCU_FAIL("Shader program object linking failed.");
}
} /* for (all shader program objects) */
} /* for (both shader program object variants) */
/* Generate a texture object. We will use the base mip-map as a render-target */
gl.genTextures(1, &m_to_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenTextures() call failed.");
gl.bindTexture(GL_TEXTURE_2D, m_to_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTexture() call failed.");
gl.texStorage2D(GL_TEXTURE_2D, 1 /* levels */, GL_RGBA32F, m_to_width, m_to_height);
GLU_EXPECT_NO_ERROR(gl.getError(), "glTexStorage2D() call failed");
/* Generate and configure a FBO we will use for the draw call */
gl.genFramebuffers(1, &m_fbo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenFramebuffers() call failed.");
gl.bindFramebuffer(GL_FRAMEBUFFER, m_fbo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer() call failed.");
gl.framebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_to_id, 0 /* level */);
GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferTexture2D() call failed.");
/* Generate & bind a VAO */
gl.genVertexArrays(1, &m_vao_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenVertexArrays() call failed.");
gl.bindVertexArray(m_vao_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray() call failed.");
/* Set up tessellation */
gl.patchParameteri(GL_PATCH_VERTICES, 1);
GLU_EXPECT_NO_ERROR(gl.getError(), "glPatchParameteri() call failed.");
/* Set up pixel storage alignment */
gl.pixelStorei(GL_PACK_ALIGNMENT, 1);
GLU_EXPECT_NO_ERROR(gl.getError(), "glPixelStorei() call failed.");
/* Allocate enough space to hold color attachment data */
m_read_buffer = (unsigned char*)new float[m_to_width * m_to_height * 4 /* rgba */];
}
/** Executes test iteration.
*
* @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed.
*/
tcu::TestNode::IterateResult FunctionalTest13::iterate()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Do not execute the test if GL_ARB_shader_subroutine and GL_ARB_separate_shader_objects
* are not supported */
if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_shader_subroutine"))
{
throw tcu::NotSupportedError("GL_ARB_shader_subroutine is not supported.");
}
if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_separate_shader_objects"))
{
throw tcu::NotSupportedError("GL_ARB_separate_shader_objects is not supported");
}
/* Initialize all GL objects before we continue */
initTest();
/* Iterate over all possible FS/GS/TC/TE/VS permutations */
for (int n_shader_permutation = 0; n_shader_permutation < 32 /* 2^5 */; ++n_shader_permutation)
{
const unsigned int n_fs_idx = ((n_shader_permutation & (1 << 0)) != 0) ? 1 : 0;
const unsigned int n_gs_idx = ((n_shader_permutation & (1 << 1)) != 0) ? 1 : 0;
const unsigned int n_tc_idx = ((n_shader_permutation & (1 << 2)) != 0) ? 1 : 0;
const unsigned int n_te_idx = ((n_shader_permutation & (1 << 3)) != 0) ? 1 : 0;
const unsigned int n_vs_idx = ((n_shader_permutation & (1 << 4)) != 0) ? 1 : 0;
const unsigned int fs_po_id = m_fs_po_ids[n_fs_idx];
const unsigned int gs_po_id = m_gs_po_ids[n_gs_idx];
const unsigned int tc_po_id = m_tc_po_ids[n_tc_idx];
const unsigned int te_po_id = m_te_po_ids[n_te_idx];
const unsigned int vs_po_id = m_vs_po_ids[n_vs_idx];
/* Configure fragment shader stage */
gl.useProgramStages(m_pipeline_id, GL_FRAGMENT_SHADER_BIT, fs_po_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgramStages() call failed for GL_FRAGMENT_SHADER_BIT bit");
/* Configure geometry shader stage */
gl.useProgramStages(m_pipeline_id, GL_GEOMETRY_SHADER_BIT, gs_po_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgramStages() call failed for GL_GEOMETRY_SHADER_BIT bit");
/* Configure tessellation control shader stage */
gl.useProgramStages(m_pipeline_id, GL_TESS_CONTROL_SHADER_BIT, tc_po_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgramStages() call failed for GL_TESS_CONTROL_SHADER_BIT bit");
/* Configure tessellation evaluation shader stage */
gl.useProgramStages(m_pipeline_id, GL_TESS_EVALUATION_SHADER_BIT, te_po_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgramStages() call failed for GL_TESS_EVALUATION_SHADER_BIT bit");
/* Configure vertex shader stage */
gl.useProgramStages(m_pipeline_id, GL_VERTEX_SHADER_BIT, vs_po_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgramStages() call failed for GL_VERTEX_SHADER_BIT bit");
/* Validate the pipeline */
glw::GLint validate_status = GL_FALSE;
gl.validateProgramPipeline(m_pipeline_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glValidateProgramPipeline() call failed.");
gl.getProgramPipelineiv(m_pipeline_id, GL_VALIDATE_STATUS, &validate_status);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramPipelineiv() call failed.");
if (validate_status != GL_TRUE)
{
TCU_FAIL("Program pipeline has not been validated successfully.");
}
/* Retrieve subroutine indices */
GLuint fs_subroutine_indices[2] = { (GLuint)-1 };
GLint fs_subroutine_uniform_index = 0;
GLuint gs_subroutine_indices[2] = { (GLuint)-1 };
GLint gs_subroutine_uniform_index = 0;
GLuint tc_subroutine_indices[2] = { (GLuint)-1 };
GLint tc_subroutine_uniform_index = 0;
GLuint te_subroutine_indices[2] = { (GLuint)-1 };
GLint te_subroutine_uniform_index = 0;
GLuint vs_subroutine_indices[2] = { (GLuint)-1 };
GLint vs_subroutine_uniform_index = 0;
for (unsigned int n_subroutine = 0; n_subroutine < 2; ++n_subroutine)
{
std::stringstream fs_subroutine_name_sstream;
std::stringstream gs_subroutine_name_sstream;
std::stringstream tc_subroutine_name_sstream;
std::stringstream te_subroutine_name_sstream;
std::stringstream vs_subroutine_name_sstream;
fs_subroutine_name_sstream << "SubroutineFS" << (n_subroutine + 1);
gs_subroutine_name_sstream << "SubroutineGS" << (n_subroutine + 1);
tc_subroutine_name_sstream << "SubroutineTC" << (n_subroutine + 1);
te_subroutine_name_sstream << "SubroutineTE" << (n_subroutine + 1);
vs_subroutine_name_sstream << "SubroutineVS" << (n_subroutine + 1);
fs_subroutine_indices[n_subroutine] =
gl.getSubroutineIndex(fs_po_id, GL_FRAGMENT_SHADER, fs_subroutine_name_sstream.str().c_str());
gs_subroutine_indices[n_subroutine] =
gl.getSubroutineIndex(gs_po_id, GL_GEOMETRY_SHADER, gs_subroutine_name_sstream.str().c_str());
tc_subroutine_indices[n_subroutine] =
gl.getSubroutineIndex(tc_po_id, GL_TESS_CONTROL_SHADER, tc_subroutine_name_sstream.str().c_str());
te_subroutine_indices[n_subroutine] =
gl.getSubroutineIndex(te_po_id, GL_TESS_EVALUATION_SHADER, te_subroutine_name_sstream.str().c_str());
vs_subroutine_indices[n_subroutine] =
gl.getSubroutineIndex(vs_po_id, GL_VERTEX_SHADER, vs_subroutine_name_sstream.str().c_str());
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetSubroutineIndex() call(s) failed.");
if (fs_subroutine_indices[n_subroutine] == (GLuint)-1 ||
gs_subroutine_indices[n_subroutine] == (GLuint)-1 ||
tc_subroutine_indices[n_subroutine] == (GLuint)-1 ||
te_subroutine_indices[n_subroutine] == (GLuint)-1 || vs_subroutine_indices[n_subroutine] == (GLuint)-1)
{
m_testCtx.getLog() << tcu::TestLog::Message
<< "At least one subroutine was not recognized by glGetSubroutineIndex() call. "
"(fs:"
<< fs_subroutine_indices[n_subroutine]
<< ", gs:" << gs_subroutine_indices[n_subroutine]
<< ", tc:" << tc_subroutine_indices[n_subroutine]
<< ", te:" << te_subroutine_indices[n_subroutine]
<< ", vs:" << vs_subroutine_indices[n_subroutine] << ")."
<< tcu::TestLog::EndMessage;
TCU_FAIL("At least one subroutine was not recognized");
}
} /* for (both subroutines) */
/* Retrieve subroutine uniform indices */
fs_subroutine_uniform_index = gl.getSubroutineUniformLocation(fs_po_id, GL_FRAGMENT_SHADER, "function");
gs_subroutine_uniform_index = gl.getSubroutineUniformLocation(gs_po_id, GL_GEOMETRY_SHADER, "function");
tc_subroutine_uniform_index = gl.getSubroutineUniformLocation(tc_po_id, GL_TESS_CONTROL_SHADER, "function");
te_subroutine_uniform_index = gl.getSubroutineUniformLocation(te_po_id, GL_TESS_EVALUATION_SHADER, "function");
vs_subroutine_uniform_index = gl.getSubroutineUniformLocation(vs_po_id, GL_VERTEX_SHADER, "function");
if (fs_subroutine_uniform_index == -1 || gs_subroutine_uniform_index == -1 ||
tc_subroutine_uniform_index == -1 || te_subroutine_uniform_index == -1 || vs_subroutine_uniform_index == -1)
{
m_testCtx.getLog() << tcu::TestLog::Message << "At least one subroutine uniform is considered inactive by "
"glGetSubroutineUniformLocation ("
"fs:"
<< fs_subroutine_uniform_index << ", gs:" << gs_subroutine_uniform_index
<< ", tc:" << tc_subroutine_uniform_index << ", te:" << te_subroutine_uniform_index
<< ", vs:" << vs_subroutine_uniform_index << ")." << tcu::TestLog::EndMessage;
TCU_FAIL("At least one subroutine uniform is considered inactive");
}
/* Check if both subroutines work correctly in each stage */
for (int n_subroutine_permutation = 0; n_subroutine_permutation < 32; /* 2^5 */
++n_subroutine_permutation)
{
unsigned int n_fs_subroutine = ((n_subroutine_permutation & (1 << 0)) != 0) ? 1 : 0;
unsigned int n_gs_subroutine = ((n_subroutine_permutation & (1 << 1)) != 0) ? 1 : 0;
unsigned int n_tc_subroutine = ((n_subroutine_permutation & (1 << 2)) != 0) ? 1 : 0;
unsigned int n_te_subroutine = ((n_subroutine_permutation & (1 << 3)) != 0) ? 1 : 0;
unsigned int n_vs_subroutine = ((n_subroutine_permutation & (1 << 4)) != 0) ? 1 : 0;
/* Configure subroutine uniforms */
struct
{
glw::GLenum stage;
glw::GLuint po_id;
glw::GLuint* indices;
} configurations[] = {
{ GL_FRAGMENT_SHADER, fs_po_id, fs_subroutine_indices + n_fs_subroutine },
{ GL_GEOMETRY_SHADER, gs_po_id, gs_subroutine_indices + n_gs_subroutine },
{ GL_TESS_CONTROL_SHADER, tc_po_id, tc_subroutine_indices + n_tc_subroutine },
{ GL_TESS_EVALUATION_SHADER, te_po_id, te_subroutine_indices + n_te_subroutine },
{ GL_VERTEX_SHADER, vs_po_id, vs_subroutine_indices + n_vs_subroutine },
};
for (int i = 0; i < 5; ++i)
{
gl.activeShaderProgram(m_pipeline_id, configurations[i].po_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glActiveShaderProgram() call failed.");
gl.uniformSubroutinesuiv(configurations[i].stage, 1 /* count */, configurations[i].indices);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUniformSubroutinesuiv() call failed.");
}
/* Render a full-screen quad with the pipeline */
gl.clear(GL_COLOR_BUFFER_BIT);
GLU_EXPECT_NO_ERROR(gl.getError(), "glClear() call failed.");
gl.drawArrays(GL_PATCHES, 0 /* first */, 1 /* count */);
GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays() call failed.");
/* Read color attachment's contents */
gl.readPixels(0, /* x */
0, /* y */
m_to_width, m_to_height, GL_RGBA, GL_FLOAT, m_read_buffer);
GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels() call failed.");
/* Verify the contents */
verifyReadBuffer(n_fs_idx, n_fs_subroutine, n_gs_idx, n_gs_subroutine, n_tc_idx, n_tc_subroutine, n_te_idx,
n_te_subroutine, n_vs_idx, n_vs_subroutine);
} /* for (all subroutine permutations) */
} /* for (all program shader object permutations) */
/** All done */
if (m_has_test_passed)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
return STOP;
}
/** Verifies the data that have been rendered using a pipeline object.
* Contents of the data depends on indices of the shaders, as well as
* on the subroutines that have been activated for particular iteration.
*
* @param n_fs_id Index of the fragment shader used for the iteration;
* @param n_fs_subroutine Index of the subroutine used in the fragment shader
* for the iteration;
* @param n_gs_id Index of the geometry shader used for the iteration;
* @param n_gs_subroutine Index of the subroutine used in the geometry shader
* for the iteration;
* @param n_tc_id Index of the tessellation control shader used for the iteration;
* @param n_tc_subroutine Index of the subroutine used in the tessellation control
* shader for the iteration;
* @param n_te_id Index of the tessellation evaluation shader used for the iteration;
* @param n_te_subroutine Index of the subroutine used in the tessellation evaluation
* shader for the iteration;
* @param n_vs_id Index of the vertex shader used for the iteration;
* @param n_vs_subroutine Index of the subroutine used in the vertex shader for
* the iteration.
*/
void FunctionalTest13::verifyReadBuffer(unsigned int n_fs_id, unsigned int n_fs_subroutine, unsigned int n_gs_id,
unsigned int n_gs_subroutine, unsigned int n_tc_id,
unsigned int n_tc_subroutine, unsigned int n_te_id,
unsigned int n_te_subroutine, unsigned int n_vs_id,
unsigned int n_vs_subroutine)
{
float expected_color[4] = { 0 };
float fs_modifier[4] = { 0 };
float gs_modifier[4] = { 0 };
float tc_modifier[4] = { 0 };
float te_modifier[4] = { 0 };
float vs_modifier[4] = { 0 };
if (n_fs_subroutine == 0)
{
for (unsigned int n_component = 0; n_component < 4; ++n_component)
{
fs_modifier[n_component] = float(n_fs_id + n_component + 1) / 10.0f;
}
}
else
{
for (unsigned int n_component = 0; n_component < 4; ++n_component)
{
fs_modifier[n_component] = float(n_fs_id + n_component + 1) / 20.0f;
}
}
if (n_gs_subroutine == 0)
{
gs_modifier[3] = float(n_gs_id + 1) * 0.425f;
}
else
{
gs_modifier[3] = float(n_gs_id + 1) * 0.0425f;
}
if (n_tc_subroutine == 0)
{
tc_modifier[1] = float(n_tc_id + 1) * 0.25f;
}
else
{
tc_modifier[1] = float(n_tc_id + 1) * 0.025f;
}
if (n_te_subroutine == 0)
{
te_modifier[2] = float(n_te_id + 1) * 0.325f;
}
else
{
te_modifier[2] = float(n_te_id + 1) * 0.0325f;
}
if (n_vs_subroutine == 0)
{
vs_modifier[0] = float(n_vs_id + 1) * 0.125f;
}
else
{
vs_modifier[0] = float(n_vs_id + 1) * 0.0125f;
}
/* Determine the expected color */
for (unsigned int n_component = 0; n_component < 4 /* rgba */; ++n_component)
{
expected_color[n_component] = fs_modifier[n_component] + gs_modifier[n_component] + tc_modifier[n_component] +
te_modifier[n_component] + vs_modifier[n_component];
}
/* Verify all read texels are valid */
const float epsilon = 1e-5f;
bool should_continue = true;
for (unsigned int y = 0; y < m_to_height && should_continue; ++y)
{
const float* row_ptr = (const float*)m_read_buffer + y * m_to_width * 4; /* rgba */
for (unsigned int x = 0; x < m_to_width && should_continue; ++x)
{
const float* texel_ptr = row_ptr + x * 4; /* rgba */
if (de::abs(texel_ptr[0] - expected_color[0]) > epsilon ||
de::abs(texel_ptr[1] - expected_color[1]) > epsilon ||
de::abs(texel_ptr[2] - expected_color[2]) > epsilon ||
de::abs(texel_ptr[3] - expected_color[3]) > epsilon)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Invalid texel rendered at (" << x << ", " << y
<< ") for "
"the following configuration: "
"n_fs_id:"
<< n_fs_id << " n_fs_subroutine:" << n_fs_subroutine << " n_gs_id:" << n_gs_id
<< " n_gs_subroutine:" << n_gs_subroutine << " n_tc_id:" << n_tc_id
<< " n_tc_subroutine:" << n_tc_subroutine << " n_te_id:" << n_te_id
<< " n_te_subroutine:" << n_te_subroutine << " n_vs_id:" << n_vs_id
<< " n_vs_subroutine:" << n_vs_subroutine << "; expected:"
"("
<< expected_color[0] << ", " << expected_color[1] << ", " << expected_color[2]
<< ", " << expected_color[3] << "), found:"
"("
<< texel_ptr[0] << ", " << texel_ptr[1] << ", " << texel_ptr[2] << ", "
<< texel_ptr[3] << ")." << tcu::TestLog::EndMessage;
m_has_test_passed = false;
should_continue = false;
}
} /* for (all columns) */
} /* for (all rows) */
}
/** Constructor
*
* @param context CTS context
**/
FunctionalTest14_15::FunctionalTest14_15(deqp::Context& context)
: TestCase(context, "structure_parameters_program_binary", "Verify structures can be used as parameters")
, m_uniform_location(0)
{
}
/** Execute test
*
* @return tcu::TestNode::STOP
**/
tcu::TestNode::IterateResult FunctionalTest14_15::iterate()
{
static const GLchar* vertex_shader_code =
"#version 400 core\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"precision highp float;\n"
"\n"
"struct data\n"
"{\n"
" uint r;\n"
" uint g;\n"
" uint b;\n"
" uint a;\n"
"};\n"
"\n"
"subroutine void routine_type_1(in data iparam, out data oparam);\n"
"subroutine void routine_type_2(inout data arg);\n"
"\n"
"subroutine (routine_type_1) void invert(in data iparam, out data oparam)\n"
"{\n"
" oparam.r = iparam.a;\n"
" oparam.g = iparam.b;\n"
" oparam.b = iparam.g;\n"
" oparam.a = iparam.r;\n"
"}\n"
"\n"
"subroutine (routine_type_1) void increment(in data iparam, out data oparam)\n"
"{\n"
" oparam.r = 1 + iparam.r;\n"
" oparam.g = 1 + iparam.g;\n"
" oparam.b = 1 + iparam.b;\n"
" oparam.a = 1 + iparam.a;\n"
"}\n"
"\n"
"subroutine (routine_type_2) void div_by_2(inout data arg)\n"
"{\n"
" arg.r = arg.r / 2;\n"
" arg.g = arg.g / 2;\n"
" arg.b = arg.b / 2;\n"
" arg.a = arg.a / 2;\n"
"}\n"
"\n"
"subroutine (routine_type_2) void decrement(inout data arg)\n"
"{\n"
" arg.r = arg.r - 1;\n"
" arg.g = arg.g - 1;\n"
" arg.b = arg.b - 1;\n"
" arg.a = arg.a - 1;\n"
"}\n"
"\n"
"subroutine uniform routine_type_1 routine_1;\n"
"subroutine uniform routine_type_2 routine_2;\n"
"\n"
"uniform uvec4 uni_input;\n"
"\n"
"out uvec4 out_routine_1;\n"
"out uvec4 out_routine_2;\n"
"\n"
"\n"
"void main()\n"
"{\n"
" data routine_1_input;\n"
" data routine_1_output;\n"
" data routine_2_arg;\n"
"\n"
" routine_1_input.r = uni_input.r;\n"
" routine_1_input.g = uni_input.g;\n"
" routine_1_input.b = uni_input.b;\n"
" routine_1_input.a = uni_input.a;\n"
"\n"
" routine_2_arg.r = uni_input.r;\n"
" routine_2_arg.g = uni_input.g;\n"
" routine_2_arg.b = uni_input.b;\n"
" routine_2_arg.a = uni_input.a;\n"
"\n"
" routine_1(routine_1_input, routine_1_output);\n"
" routine_2(routine_2_arg);\n"
"\n"
" out_routine_1.r = routine_1_output.r;\n"
" out_routine_1.g = routine_1_output.g;\n"
" out_routine_1.b = routine_1_output.b;\n"
" out_routine_1.a = routine_1_output.a;\n"
"\n"
" out_routine_2.r = routine_2_arg.r;\n"
" out_routine_2.g = routine_2_arg.g;\n"
" out_routine_2.b = routine_2_arg.b;\n"
" out_routine_2.a = routine_2_arg.a;\n"
"}\n"
"\n";
static const GLchar* subroutine_names[][2] = { { "invert", "increment" }, { "div_by_2", "decrement" } };
static const GLuint n_subroutine_types = sizeof(subroutine_names) / sizeof(subroutine_names[0]);
static const GLchar* subroutine_uniform_names[] = { "routine_1", "routine_2" };
static const GLuint n_subroutine_uniform_names =
sizeof(subroutine_uniform_names) / sizeof(subroutine_uniform_names[0]);
static const GLchar* uniform_name = "uni_input";
static const GLchar* varying_names[] = { "out_routine_1", "out_routine_2" };
static const GLuint n_varying_names = sizeof(varying_names) / sizeof(varying_names[0]);
static const GLuint transform_feedback_buffer_size = n_varying_names * 4 * sizeof(GLuint);
/* Test data */
static const Utils::vec4<GLuint> uni_input[] = { Utils::vec4<GLuint>(8, 64, 4096, 16777216),
Utils::vec4<GLuint>(8, 64, 4096, 16777216) };
static const Utils::vec4<GLuint> out_routine_1[] = { Utils::vec4<GLuint>(16777216, 4096, 64, 8),
Utils::vec4<GLuint>(9, 65, 4097, 16777217) };
static const Utils::vec4<GLuint> out_routine_2[] = { Utils::vec4<GLuint>(4, 32, 2048, 8388608),
Utils::vec4<GLuint>(7, 63, 4095, 16777215) };
static const GLuint n_test_cases = 2;
/* Do not execute the test if GL_ARB_shader_subroutine is not supported */
if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_shader_subroutine"))
{
throw tcu::NotSupportedError("GL_ARB_shader_subroutine is not supported.");
}
/* GL objects */
Utils::program program(m_context);
Utils::buffer transform_feedback_buffer(m_context);
Utils::vertexArray vao(m_context);
bool is_program_binary_supported = program.isProgramBinarySupported();
/* Init GL objects */
program.build(0 /* cs */, 0 /* fs */, 0 /* gs */, 0 /* tcs */, 0 /* test */, vertex_shader_code,
varying_names /* varying_names */, n_varying_names /* n_varyings */);
/* Do not execute the test if GL_ARB_get_program_binary is not supported */
if (true == is_program_binary_supported)
{
/* Get subroutine indices */
for (GLuint type = 0; type < n_subroutine_types; ++type)
{
m_initial_subroutine_indices[type][0] =
program.getSubroutineIndex(subroutine_names[type][0], GL_VERTEX_SHADER);
m_initial_subroutine_indices[type][1] =
program.getSubroutineIndex(subroutine_names[type][1], GL_VERTEX_SHADER);
}
/* Get subroutine uniform locations */
for (GLuint uniform = 0; uniform < n_subroutine_uniform_names; ++uniform)
{
m_initial_subroutine_uniform_locations[uniform] =
program.getSubroutineUniformLocation(subroutine_uniform_names[uniform], GL_VERTEX_SHADER);
}
/* Delete program and recreate it from binary */
std::vector<GLubyte> program_binary;
GLenum binary_format;
program.getBinary(program_binary, binary_format);
program.remove();
program.createFromBinary(program_binary, binary_format);
}
program.use();
vao.generate();
vao.bind();
transform_feedback_buffer.generate();
transform_feedback_buffer.update(GL_TRANSFORM_FEEDBACK_BUFFER, transform_feedback_buffer_size, 0 /* data */,
GL_DYNAMIC_COPY);
transform_feedback_buffer.bindRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0, transform_feedback_buffer_size);
/* Get subroutine indices */
for (GLuint type = 0; type < n_subroutine_types; ++type)
{
m_subroutine_indices[type][0] = program.getSubroutineIndex(subroutine_names[type][0], GL_VERTEX_SHADER);
m_subroutine_indices[type][1] = program.getSubroutineIndex(subroutine_names[type][1], GL_VERTEX_SHADER);
}
/* Get subroutine uniform locations */
for (GLuint uniform = 0; uniform < n_subroutine_uniform_names; ++uniform)
{
m_subroutine_uniform_locations[uniform] =
program.getSubroutineUniformLocation(subroutine_uniform_names[uniform], GL_VERTEX_SHADER);
}
/* Get uniform locations */
m_uniform_location = program.getUniformLocation(uniform_name);
/* Test */
bool result = true;
/* Test program binary */
if (true == is_program_binary_supported)
{
/* Test indices and locations */
if (false == testIndicesAndLocations())
{
static const GLuint n_subroutines_per_type = 2;
m_context.getTestContext().getLog() << tcu::TestLog::Message
<< "Error. Subroutine indices or subroutine uniform location changed."
<< tcu::TestLog::EndMessage;
for (GLuint type = 0; type < n_subroutine_types; ++type)
{
for (GLuint i = 0; i < n_subroutines_per_type; ++i)
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "Subroutine: " << subroutine_names[type][i]
<< " index: " << m_subroutine_indices[type][i]
<< " initial index: " << m_initial_subroutine_indices[type][i] << tcu::TestLog::EndMessage;
}
}
for (GLuint uniform = 0; uniform < n_subroutine_uniform_names; ++uniform)
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "Subroutine uniform: " << subroutine_uniform_names[uniform]
<< " location: " << m_subroutine_uniform_locations[uniform]
<< " initial location: " << m_initial_subroutine_uniform_locations[uniform]
<< tcu::TestLog::EndMessage;
}
result = false;
}
/* Test draw with deafult set of subroutines */
if (false == testDefaultSubroutineSet(uni_input[0], out_routine_1, out_routine_2))
{
result = false;
}
}
for (GLuint i = 0; i < n_test_cases; ++i)
{
if (false == testDraw(i, uni_input[i], out_routine_1[i], out_routine_2[i]))
{
result = false;
}
}
/* Set result */
if (true == result)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
/* Done */
return tcu::TestNode::STOP;
}
/** Execute draw call and verify results
*
* @param uni_input Input data
* @param expected_routine_1_result Set of expected results of "routine_1"
* @param expected_routine_2_result Set of expected results of "routine_2"
*
* @return true if test pass, false otherwise
**/
bool FunctionalTest14_15::testDefaultSubroutineSet(const Utils::vec4<glw::GLuint>& uni_input,
const Utils::vec4<glw::GLuint> expected_routine_1_result[2],
const Utils::vec4<glw::GLuint> expected_routine_2_result[2]) const
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
bool result = true;
/* Set up input data uniforms */
gl.uniform4ui(m_uniform_location, uni_input.m_x, uni_input.m_y, uni_input.m_z, uni_input.m_w);
GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform4f");
/* Execute draw call with transform feedback */
gl.beginTransformFeedback(GL_POINTS);
GLU_EXPECT_NO_ERROR(gl.getError(), "BeginTransformFeedback");
gl.drawArrays(GL_POINTS, 0 /* first */, 1 /* count */);
GLU_EXPECT_NO_ERROR(gl.getError(), "DrawArrays");
gl.endTransformFeedback();
GLU_EXPECT_NO_ERROR(gl.getError(), "EndTransformFeedback");
/* Capture results */
GLuint* feedback_data = (GLuint*)gl.mapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, GL_READ_ONLY);
GLU_EXPECT_NO_ERROR(gl.getError(), "MapBuffer");
Utils::vec4<GLuint> routine_1_result;
Utils::vec4<GLuint> routine_2_result;
routine_1_result.m_x = feedback_data[0 + 0];
routine_1_result.m_y = feedback_data[0 + 1];
routine_1_result.m_z = feedback_data[0 + 2];
routine_1_result.m_w = feedback_data[0 + 3];
routine_2_result.m_x = feedback_data[4 + 0];
routine_2_result.m_y = feedback_data[4 + 1];
routine_2_result.m_z = feedback_data[4 + 2];
routine_2_result.m_w = feedback_data[4 + 3];
/* Unmap buffer */
gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
GLU_EXPECT_NO_ERROR(gl.getError(), "UnmapBuffer");
/* Verifiy */
result = result &&
((routine_1_result == expected_routine_1_result[0]) || (routine_1_result == expected_routine_1_result[1]));
result = result &&
((routine_2_result == expected_routine_2_result[0]) || (routine_2_result == expected_routine_2_result[1]));
/* Log error if any */
if (false == result)
{
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Error. Invalid result."
<< tcu::TestLog::EndMessage;
tcu::MessageBuilder message = m_context.getTestContext().getLog() << tcu::TestLog::Message;
message << "Routine_1, result: ";
routine_1_result.log(message);
message << "Routine_2, result: ";
routine_2_result.log(message);
message << tcu::TestLog::EndMessage;
}
/* Done */
return result;
}
/** Execute draw call and verify results
*
* @param routine_configuration Subroutine "type" ordinal
* @param uni_input Input data
* @param expected_routine_1_result Expected results of "routine_1"
* @param expected_routine_2_result Expected results of "routine_2"
*
* @return true if test pass, false otherwise
**/
bool FunctionalTest14_15::testDraw(glw::GLuint routine_configuration, const Utils::vec4<glw::GLuint>& uni_input,
const Utils::vec4<glw::GLuint>& expected_routine_1_result,
const Utils::vec4<glw::GLuint>& expected_routine_2_result) const
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
bool result = true;
GLuint subroutine_indices[2];
static const GLuint n_subroutine_uniforms = sizeof(subroutine_indices) / sizeof(subroutine_indices[0]);
/* Set up input data uniforms */
gl.uniform4ui(m_uniform_location, uni_input.m_x, uni_input.m_y, uni_input.m_z, uni_input.m_w);
GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform4f");
/* Prepare subroutine uniform data */
for (GLuint i = 0; i < n_subroutine_uniforms; ++i)
{
const GLuint location = m_subroutine_uniform_locations[i];
subroutine_indices[location] = m_subroutine_indices[i][routine_configuration];
}
/* Set up subroutine uniforms */
gl.uniformSubroutinesuiv(GL_VERTEX_SHADER, n_subroutine_uniforms, &subroutine_indices[0]);
GLU_EXPECT_NO_ERROR(gl.getError(), "UniformSubroutinesuiv");
/* Execute draw call with transform feedback */
gl.beginTransformFeedback(GL_POINTS);
GLU_EXPECT_NO_ERROR(gl.getError(), "BeginTransformFeedback");
gl.drawArrays(GL_POINTS, 0 /* first */, 1 /* count */);
GLU_EXPECT_NO_ERROR(gl.getError(), "DrawArrays");
gl.endTransformFeedback();
GLU_EXPECT_NO_ERROR(gl.getError(), "EndTransformFeedback");
/* Capture results */
GLuint* feedback_data = (GLuint*)gl.mapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, GL_READ_ONLY);
GLU_EXPECT_NO_ERROR(gl.getError(), "MapBuffer");
Utils::vec4<GLuint> routine_1_result;
Utils::vec4<GLuint> routine_2_result;
routine_1_result.m_x = feedback_data[0 + 0];
routine_1_result.m_y = feedback_data[0 + 1];
routine_1_result.m_z = feedback_data[0 + 2];
routine_1_result.m_w = feedback_data[0 + 3];
routine_2_result.m_x = feedback_data[4 + 0];
routine_2_result.m_y = feedback_data[4 + 1];
routine_2_result.m_z = feedback_data[4 + 2];
routine_2_result.m_w = feedback_data[4 + 3];
/* Unmap buffer */
gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
GLU_EXPECT_NO_ERROR(gl.getError(), "UnmapBuffer");
/* Verifiy */
result = result && (routine_1_result == expected_routine_1_result);
result = result && (routine_2_result == expected_routine_2_result);
/* Log error if any */
if (false == result)
{
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Error. Invalid result."
<< tcu::TestLog::EndMessage;
tcu::MessageBuilder message = m_context.getTestContext().getLog() << tcu::TestLog::Message;
message << "Routine_1, result: ";
routine_1_result.log(message);
message << ", expected: ";
expected_routine_1_result.log(message);
message << "Routine_2, result: ";
routine_2_result.log(message);
message << ", expected: ";
expected_routine_2_result.log(message);
message << tcu::TestLog::EndMessage;
}
/* Done */
return result;
}
/** Verify initial and current values of subroutine indices and subroutines uniform locations
*
* @return true if test pass, false otherwise
**/
bool FunctionalTest14_15::testIndicesAndLocations() const
{
static const GLuint n_subroutine_types = 2;
bool result = true;
/* Verify subroutine indices */
for (GLuint type = 0; type < n_subroutine_types; ++type)
{
result = result && (m_subroutine_indices[type][0] == m_initial_subroutine_indices[type][0]);
result = result && (m_subroutine_indices[type][1] == m_initial_subroutine_indices[type][1]);
}
/* Verify subroutine uniform locations */
for (GLuint uniform = 0; uniform < n_subroutine_types; ++uniform)
{
result = result && (m_subroutine_uniform_locations[uniform] == m_initial_subroutine_uniform_locations[uniform]);
}
return result;
}
/** Constructor.
*
* @param context Rendering context.
*
**/
FunctionalTest16::FunctionalTest16(deqp::Context& context)
: TestCase(context, "subroutine_uniform_reset",
"Checks that when the active program for a shader stage is re-linke or "
"changed by a call to UseProgram, BindProgramPipeline, or UseProgramStages,"
" subroutine uniforms for that stage are reset to arbitrarily chosen default "
"functions with compatible subroutine types.")
, m_are_pipeline_objects_supported(false)
, m_has_test_passed(true)
{
memset(m_fs_ids, 0, sizeof(m_fs_ids));
memset(m_gs_ids, 0, sizeof(m_gs_ids));
memset(m_po_ids, 0, sizeof(m_po_ids));
memset(m_tc_ids, 0, sizeof(m_tc_ids));
memset(m_te_ids, 0, sizeof(m_te_ids));
memset(m_vs_ids, 0, sizeof(m_vs_ids));
memset(m_fs_po_ids, 0, sizeof(m_fs_po_ids));
memset(m_gs_po_ids, 0, sizeof(m_gs_po_ids));
memset(m_pipeline_object_ids, 0, sizeof(m_pipeline_object_ids));
memset(m_tc_po_ids, 0, sizeof(m_tc_po_ids));
memset(m_te_po_ids, 0, sizeof(m_te_po_ids));
memset(m_vs_po_ids, 0, sizeof(m_vs_po_ids));
}
/** Deinitializes all GL objects that may have been created during test execution. */
void FunctionalTest16::deinit()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
for (unsigned int n_id = 0; n_id < 2; ++n_id)
{
if (m_fs_ids[n_id] != 0)
{
gl.deleteShader(m_fs_ids[n_id]);
m_fs_ids[n_id] = 0;
}
if (m_fs_po_ids[n_id] != 0)
{
gl.deleteProgram(m_fs_po_ids[n_id]);
m_fs_po_ids[n_id] = 0;
}
if (m_gs_ids[n_id] != 0)
{
gl.deleteShader(m_gs_ids[n_id]);
m_gs_ids[n_id] = 0;
}
if (m_gs_po_ids[n_id] != 0)
{
gl.deleteProgram(m_gs_po_ids[n_id]);
m_gs_po_ids[n_id] = 0;
}
if (m_pipeline_object_ids[n_id] != 0)
{
gl.deleteProgramPipelines(1 /* n */, m_pipeline_object_ids + n_id);
}
if (m_po_ids[n_id] != 0)
{
gl.deleteProgram(m_po_ids[n_id]);
m_po_ids[n_id] = 0;
}
if (m_tc_ids[n_id] != 0)
{
gl.deleteShader(m_tc_ids[n_id]);
m_tc_ids[n_id] = 0;
}
if (m_tc_po_ids[n_id] != 0)
{
gl.deleteProgram(m_tc_po_ids[n_id]);
m_tc_po_ids[n_id] = 0;
}
if (m_te_ids[n_id] != 0)
{
gl.deleteShader(m_te_ids[n_id]);
m_te_ids[n_id] = 0;
}
if (m_te_po_ids[n_id] != 0)
{
gl.deleteProgram(m_te_po_ids[n_id]);
m_te_po_ids[n_id] = 0;
}
if (m_vs_ids[n_id] != 0)
{
gl.deleteShader(m_vs_ids[n_id]);
m_vs_ids[n_id] = 0;
}
if (m_vs_po_ids[n_id] != 0)
{
gl.deleteProgram(m_vs_po_ids[n_id]);
m_vs_po_ids[n_id] = 0;
}
} /* for (both IDs) */
}
/** Retrieves body of a shader that should be used for user-specified shader stage.
* This function returns slightly different implementations, depending on index of
* the program/pipeline object the shader will be used for.
*
* @param shader_stage Stage the shader body is to be returned for.
* @param n_id Index of the shader (as per description).
*
* @return Requested string.
**/
std::string FunctionalTest16::getShaderBody(const Utils::_shader_stage& shader_stage, const unsigned int& n_id) const
{
std::stringstream result_sstream;
result_sstream << "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n";
switch (shader_stage)
{
case Utils::SHADER_STAGE_VERTEX:
{
result_sstream << "out gl_PerVertex { vec4 gl_Position; } ;\n";
break;
}
case Utils::SHADER_STAGE_GEOMETRY:
{
result_sstream << "layout(points) in;\n"
"layout(points, max_vertices = 1) out;\n";
result_sstream << "in gl_PerVertex { vec4 gl_Position; } gl_in[];\n";
result_sstream << "out gl_PerVertex { vec4 gl_Position; } ;\n";
break;
}
case Utils::SHADER_STAGE_TESSELLATION_CONTROL:
{
result_sstream << "layout(vertices = 4) out;\n";
result_sstream << "in gl_PerVertex { vec4 gl_Position; } gl_in[];\n";
result_sstream << "out gl_PerVertex { vec4 gl_Position; } gl_out[];\n";
break;
}
case Utils::SHADER_STAGE_TESSELLATION_EVALUATION:
{
result_sstream << "layout(quads) in;\n";
result_sstream << "in gl_PerVertex { vec4 gl_Position; } gl_in[];\n";
result_sstream << "out gl_PerVertex { vec4 gl_Position; };\n";
break;
}
default:
break;
} /* switch (shader_stage) */
result_sstream << "\n"
"subroutine void subroutineType (inout vec4 result);\n"
"subroutine vec4 subroutineType2(in vec4 data);\n"
"\n"
"subroutine(subroutineType) void function1(inout vec4 result)\n"
"{\n"
" result += vec4("
<< (n_id + 1) << ", " << (n_id + 2) << ", " << (n_id + 3) << ", " << (n_id + 4)
<< ");\n"
"}\n"
"subroutine(subroutineType) void function2(inout vec4 result)\n"
"{\n"
" result += vec4("
<< (n_id + 2) << ", " << (n_id + 3) << ", " << (n_id + 4) << ", " << (n_id + 5)
<< ");\n"
"}\n"
"\n"
"subroutine(subroutineType2) vec4 function3(in vec4 data)\n"
"{\n"
" return data * data;\n"
"}\n"
"subroutine(subroutineType2) vec4 function4(in vec4 data)\n"
"{\n"
" return data + data;\n"
"}\n"
"\n"
"subroutine uniform subroutineType subroutine1;\n"
"subroutine uniform subroutineType subroutine2;\n"
"subroutine uniform subroutineType2 subroutine3;\n"
"subroutine uniform subroutineType2 subroutine4;\n"
"\n";
if (shader_stage == Utils::SHADER_STAGE_FRAGMENT)
{
result_sstream << "out vec4 result;\n";
}
result_sstream << "void main()\n"
"{\n";
switch (shader_stage)
{
case Utils::SHADER_STAGE_FRAGMENT:
{
result_sstream << " result = vec4(0);\n"
<< " subroutine1(result);\n"
" subroutine2(result);\n"
" result = subroutine3(result) + subroutine4(result);\n";
break;
}
case Utils::SHADER_STAGE_GEOMETRY:
{
result_sstream << " gl_Position = vec4(0);\n"
" subroutine1(gl_Position);\n"
" subroutine2(gl_Position);\n"
" gl_Position = subroutine3(gl_Position) + subroutine4(gl_Position);\n"
" EmitVertex();\n";
break;
}
case Utils::SHADER_STAGE_TESSELLATION_CONTROL:
{
result_sstream << " gl_out[gl_InvocationID].gl_Position = vec4(0);\n"
" subroutine1(gl_out[gl_InvocationID].gl_Position);\n"
" subroutine2(gl_out[gl_InvocationID].gl_Position);\n"
" gl_out[gl_InvocationID].gl_Position = subroutine3(gl_in[0].gl_Position) + "
"subroutine4(gl_in[0].gl_Position);\n";
break;
}
case Utils::SHADER_STAGE_VERTEX:
case Utils::SHADER_STAGE_TESSELLATION_EVALUATION:
{
result_sstream << " gl_Position = vec4(0);\n"
" subroutine1(gl_Position);\n"
" subroutine2(gl_Position);\n"
" gl_Position = subroutine3(gl_Position) + subroutine4(gl_Position);\n";
break;
}
default:
break;
} /* switch (shader_stage) */
result_sstream << "}\n";
return result_sstream.str();
}
/** Initializes all objects required to run the test. */
void FunctionalTest16::initTest()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
for (unsigned int n_id = 0; n_id < 2 /* test program/shader objects */; ++n_id)
{
const std::string fs_body = getShaderBody(Utils::SHADER_STAGE_FRAGMENT, n_id);
const std::string gs_body = getShaderBody(Utils::SHADER_STAGE_GEOMETRY, n_id);
const std::string tc_body = getShaderBody(Utils::SHADER_STAGE_TESSELLATION_CONTROL, n_id);
const std::string te_body = getShaderBody(Utils::SHADER_STAGE_TESSELLATION_EVALUATION, n_id);
const std::string vs_body = getShaderBody(Utils::SHADER_STAGE_VERTEX, n_id);
if (!Utils::buildProgram(gl, vs_body, tc_body, te_body, gs_body, fs_body, DE_NULL, /* xfb_varyings */
DE_NULL, /* n_xfb_varyings */
m_vs_ids + n_id, m_tc_ids + n_id, m_te_ids + n_id, m_gs_ids + n_id, m_fs_ids + n_id,
m_po_ids + n_id))
{
m_testCtx.getLog() << tcu::TestLog::Message << "Failed to build test program object, index:"
"["
<< n_id << "]" << tcu::TestLog::EndMessage;
TCU_FAIL("Failed to build a test program");
}
if (m_are_pipeline_objects_supported)
{
/* Initialize shader program objects */
const char* fs_body_raw_ptr = fs_body.c_str();
const char* gs_body_raw_ptr = gs_body.c_str();
glw::GLint link_status[5] = { GL_FALSE };
const char* tc_body_raw_ptr = tc_body.c_str();
const char* te_body_raw_ptr = te_body.c_str();
const char* vs_body_raw_ptr = vs_body.c_str();
m_fs_po_ids[n_id] = gl.createShaderProgramv(GL_FRAGMENT_SHADER, 1 /* count */, &fs_body_raw_ptr);
m_gs_po_ids[n_id] = gl.createShaderProgramv(GL_GEOMETRY_SHADER, 1 /* count */, &gs_body_raw_ptr);
m_tc_po_ids[n_id] = gl.createShaderProgramv(GL_TESS_CONTROL_SHADER, 1 /* count */, &tc_body_raw_ptr);
m_te_po_ids[n_id] = gl.createShaderProgramv(GL_TESS_EVALUATION_SHADER, 1 /* count */, &te_body_raw_ptr);
m_vs_po_ids[n_id] = gl.createShaderProgramv(GL_VERTEX_SHADER, 1 /* count */, &vs_body_raw_ptr);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShaderProgramv() call failed.");
gl.getProgramiv(m_fs_po_ids[n_id], GL_LINK_STATUS, link_status + 0);
gl.getProgramiv(m_gs_po_ids[n_id], GL_LINK_STATUS, link_status + 1);
gl.getProgramiv(m_tc_po_ids[n_id], GL_LINK_STATUS, link_status + 2);
gl.getProgramiv(m_te_po_ids[n_id], GL_LINK_STATUS, link_status + 3);
gl.getProgramiv(m_vs_po_ids[n_id], GL_LINK_STATUS, link_status + 4);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv() call failed.");
if (link_status[0] == GL_FALSE)
TCU_FAIL("Fragment shader program failed to link");
if (link_status[1] == GL_FALSE)
TCU_FAIL("Geometry shader program failed to link");
if (link_status[2] == GL_FALSE)
TCU_FAIL("Tessellation control shader program failed to link");
if (link_status[3] == GL_FALSE)
TCU_FAIL("Tessellation evaluation shader program failed to link");
if (link_status[4] == GL_FALSE)
TCU_FAIL("Vertex shader program failed to link");
/* Initialize pipeline program object */
gl.genProgramPipelines(1 /* n */, m_pipeline_object_ids + n_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenProgramPipelines() call failed.");
gl.useProgramStages(m_pipeline_object_ids[n_id], GL_FRAGMENT_SHADER_BIT, m_fs_po_ids[n_id]);
gl.useProgramStages(m_pipeline_object_ids[n_id], GL_GEOMETRY_SHADER_BIT, m_gs_po_ids[n_id]);
gl.useProgramStages(m_pipeline_object_ids[n_id], GL_TESS_CONTROL_SHADER_BIT, m_tc_po_ids[n_id]);
gl.useProgramStages(m_pipeline_object_ids[n_id], GL_TESS_EVALUATION_SHADER_BIT, m_te_po_ids[n_id]);
gl.useProgramStages(m_pipeline_object_ids[n_id], GL_VERTEX_SHADER_BIT, m_vs_po_ids[n_id]);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgramStages() call failed.");
}
/* Retrieve subroutine locations */
struct _item
{
glw::GLuint po_id;
_shader_stage& stage;
glw::GLuint so_id;
glw::GLenum so_type;
} items[] = {
{ m_po_ids[n_id], m_po_descriptors[n_id].fragment, m_fs_ids[n_id], GL_FRAGMENT_SHADER },
{ m_po_ids[n_id], m_po_descriptors[n_id].geometry, m_gs_ids[n_id], GL_GEOMETRY_SHADER },
{ m_po_ids[n_id], m_po_descriptors[n_id].tess_control, m_tc_ids[n_id], GL_TESS_CONTROL_SHADER },
{ m_po_ids[n_id], m_po_descriptors[n_id].tess_evaluation, m_te_ids[n_id], GL_TESS_EVALUATION_SHADER },
{ m_po_ids[n_id], m_po_descriptors[n_id].vertex, m_vs_ids[n_id], GL_VERTEX_SHADER },
{ m_fs_po_ids[n_id], m_fs_po_descriptors[n_id], m_fs_po_ids[n_id], GL_FRAGMENT_SHADER },
{ m_gs_po_ids[n_id], m_gs_po_descriptors[n_id], m_gs_po_ids[n_id], GL_GEOMETRY_SHADER },
{ m_tc_po_ids[n_id], m_tc_po_descriptors[n_id], m_tc_po_ids[n_id], GL_TESS_CONTROL_SHADER },
{ m_te_po_ids[n_id], m_te_po_descriptors[n_id], m_te_po_ids[n_id], GL_TESS_EVALUATION_SHADER },
{ m_vs_po_ids[n_id], m_vs_po_descriptors[n_id], m_vs_po_ids[n_id], GL_VERTEX_SHADER },
};
const unsigned int n_items = sizeof(items) / sizeof(items[0]);
for (unsigned int n_item = 0; n_item < n_items; ++n_item)
{
_item& current_item = items[n_item];
current_item.stage.function1_index =
gl.getSubroutineIndex(current_item.po_id, current_item.so_type, "function1");
current_item.stage.function2_index =
gl.getSubroutineIndex(current_item.po_id, current_item.so_type, "function2");
current_item.stage.function3_index =
gl.getSubroutineIndex(current_item.po_id, current_item.so_type, "function3");
current_item.stage.function4_index =
gl.getSubroutineIndex(current_item.po_id, current_item.so_type, "function4");
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetSubroutineIndex() call(s) failed.");
if (current_item.stage.function1_index == GL_INVALID_INDEX ||
current_item.stage.function2_index == GL_INVALID_INDEX ||
current_item.stage.function3_index == GL_INVALID_INDEX ||
current_item.stage.function4_index == GL_INVALID_INDEX)
{
TCU_FAIL("Subroutine name was not recognized.");
}
current_item.stage.subroutine1_uniform_location =
gl.getSubroutineUniformLocation(current_item.po_id, current_item.so_type, "subroutine1");
current_item.stage.subroutine2_uniform_location =
gl.getSubroutineUniformLocation(current_item.po_id, current_item.so_type, "subroutine2");
current_item.stage.subroutine3_uniform_location =
gl.getSubroutineUniformLocation(current_item.po_id, current_item.so_type, "subroutine3");
current_item.stage.subroutine4_uniform_location =
gl.getSubroutineUniformLocation(current_item.po_id, current_item.so_type, "subroutine4");
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetSubroutineUniformLocation() call(s) failed.");
if (current_item.stage.subroutine1_uniform_location == -1 ||
current_item.stage.subroutine2_uniform_location == -1 ||
current_item.stage.subroutine3_uniform_location == -1 ||
current_item.stage.subroutine4_uniform_location == -1)
{
TCU_FAIL("Subroutine uniform name was not recognized.");
}
if (m_po_ids[n_id] == current_item.po_id)
{
gl.useProgram(current_item.po_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() call failed.");
}
else
{
/* Temporarily bind the program pipeline. */
gl.bindProgramPipeline(m_pipeline_object_ids[n_id]);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindProgramPipeline() call failed.");
}
gl.getUniformSubroutineuiv(current_item.so_type, current_item.stage.subroutine1_uniform_location,
&current_item.stage.default_subroutine1_value);
gl.getUniformSubroutineuiv(current_item.so_type, current_item.stage.subroutine2_uniform_location,
&current_item.stage.default_subroutine2_value);
gl.getUniformSubroutineuiv(current_item.so_type, current_item.stage.subroutine3_uniform_location,
&current_item.stage.default_subroutine3_value);
gl.getUniformSubroutineuiv(current_item.so_type, current_item.stage.subroutine4_uniform_location,
&current_item.stage.default_subroutine4_value);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetUniformSubroutineuiv() call(s) failed.");
current_item.stage.gl_stage = current_item.so_type;
if (m_po_ids[n_id] != current_item.po_id)
{
/* Unbind the program pipeline object */
gl.bindProgramPipeline(0);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindProgramPipeline() call failed.");
}
} /* for (all items) */
/* Make sure the default subroutine choices are valid. */
verifySubroutineUniformValues(
TEST_CASE_SWITCH_TO_DIFFERENT_PROGRAM_OBJECT, /* makes the verification routine use program object descriptor */
n_id, SUBROUTINE_UNIFORMS_SET_TO_VALID_VALUES);
if (m_are_pipeline_objects_supported)
{
gl.useProgram(0);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() call failed.");
gl.bindProgramPipeline(m_pipeline_object_ids[n_id]);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindProgramPipeline() call failed.");
{
verifySubroutineUniformValues(
TEST_CASE_SWITCH_TO_DIFFERENT_PROGRAM_PIPELINE_OBJECT, /* makes the verification routine use pipeline object descriptor */
n_id, SUBROUTINE_UNIFORMS_SET_TO_VALID_VALUES);
}
gl.bindProgramPipeline(0);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindProgramPipeline() call failed.");
}
} /* for (both program descriptors) */
}
/** Retrieves IDs of shaders OR shader program objects, depending on which of the two
* the caller requests for.
*
* @param retrieve_program_object_shader_ids true if the caller wishes to retrieve shader object IDs,
* false to return shader program IDs.
* @param n_id Index of the program/pipeline object the shaders
* are a part of.
* @param out_shader_stages Deref will be used to store exactly five IDs. Must not
* be NULL.
**/
void FunctionalTest16::getShaderStages(bool retrieve_program_object_shader_ids, const unsigned int& n_id,
const _shader_stage** out_shader_stages) const
{
if (retrieve_program_object_shader_ids)
{
out_shader_stages[0] = &m_po_descriptors[n_id].vertex;
out_shader_stages[1] = &m_po_descriptors[n_id].tess_control;
out_shader_stages[2] = &m_po_descriptors[n_id].tess_evaluation;
out_shader_stages[3] = &m_po_descriptors[n_id].geometry;
out_shader_stages[4] = &m_po_descriptors[n_id].fragment;
}
else
{
out_shader_stages[0] = m_vs_po_descriptors + n_id;
out_shader_stages[1] = m_tc_po_descriptors + n_id;
out_shader_stages[2] = m_te_po_descriptors + n_id;
out_shader_stages[3] = m_gs_po_descriptors + n_id;
out_shader_stages[4] = m_fs_po_descriptors + n_id;
}
}
/** Executes test iteration.
*
* @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed.
*/
tcu::TestNode::IterateResult FunctionalTest16::iterate()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Do not execute the test if GL_ARB_shader_subroutine is not supported */
if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_shader_subroutine"))
{
throw tcu::NotSupportedError("GL_ARB_shader_subroutine is not supported.");
}
m_are_pipeline_objects_supported =
m_context.getContextInfo().isExtensionSupported("GL_ARB_separate_shader_objects");
/* Initialize GL objects required to run the test */
initTest();
/* Iterate over both pipelines/programs and verify that calling glUseProgram() /
* glBindProgramPipeline() / glUseProgramStages() resets subroutine uniform configuration.
*/
for (int test_case = static_cast<int>(TEST_CASE_FIRST); test_case != static_cast<int>(TEST_CASE_COUNT); ++test_case)
{
if (static_cast<_test_case>(test_case) != TEST_CASE_SWITCH_TO_DIFFERENT_PROGRAM_OBJECT &&
!m_are_pipeline_objects_supported)
{
/* Current test case requires GL_ARB_separate_shader_objects support which is
* unavaiable on the platform that we're testing
*/
continue;
}
for (unsigned int n_object_id = 0; n_object_id < 2; /* pipeline/program objects allocated for the test */
++n_object_id)
{
/* Verify that currently reported subroutine uniform values are equal to default values */
if (test_case == TEST_CASE_SWITCH_TO_DIFFERENT_PROGRAM_OBJECT)
{
gl.useProgram(m_po_ids[n_object_id]);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() call failed");
}
else
{
gl.bindProgramPipeline(m_pipeline_object_ids[n_object_id]);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindProgramPipeline() call failed");
}
verifySubroutineUniformValues(static_cast<_test_case>(test_case), n_object_id,
SUBROUTINE_UNIFORMS_SET_TO_DEFAULT_VALUES);
/* Re-configure subroutine uniforms so that they point to different subroutines than
* the default ones.
*/
const _shader_stage* stages[5 /* fs+gs+tc+te+vs */] = { DE_NULL };
getShaderStages(static_cast<_test_case>(test_case) == TEST_CASE_SWITCH_TO_DIFFERENT_PROGRAM_OBJECT,
n_object_id, stages);
for (unsigned int n_stage = 0; n_stage < 5 /* fs+gs+tc+te+vs stages */; ++n_stage)
{
const _shader_stage& current_stage = *(stages[n_stage]);
glw::GLuint subroutine_configuration[4] = { GL_INVALID_INDEX };
subroutine_configuration[0] =
(current_stage.default_subroutine1_value == current_stage.function1_index) ?
current_stage.function2_index :
current_stage.function1_index;
subroutine_configuration[1] =
(current_stage.default_subroutine2_value == current_stage.function1_index) ?
current_stage.function2_index :
current_stage.function1_index;
subroutine_configuration[2] =
(current_stage.default_subroutine3_value == current_stage.function3_index) ?
current_stage.function4_index :
current_stage.function3_index;
subroutine_configuration[3] =
(current_stage.default_subroutine4_value == current_stage.function3_index) ?
current_stage.function4_index :
current_stage.function3_index;
gl.uniformSubroutinesuiv(current_stage.gl_stage, 4 /* count */, subroutine_configuration);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUniformSubroutinesuiv() call failed.");
} /* for (all stages) */
verifySubroutineUniformValues(static_cast<_test_case>(test_case), n_object_id,
SUBROUTINE_UNIFORMS_SET_TO_NONDEFAULT_VALUES);
/* Execute test case-specific code */
_shader_stage cached_shader_stage_data;
bool stage_reset_status[Utils::SHADER_STAGE_COUNT] = { false, false, false, false, false };
bool uses_stage_reset_status = false;
switch (test_case)
{
case TEST_CASE_SWITCH_TO_DIFFERENT_PROGRAM_OBJECT:
{
/* Switch to a different program object and then back to current PO.
* Subroutine uniforms should be back at their default settings, instead of
* the ones we've just set.
*/
gl.useProgram(m_po_ids[(n_object_id + 1) % 2 /* objects allocated for the test */]);
gl.useProgram(m_po_ids[n_object_id]);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() call(s) failed.");
break;
}
case TEST_CASE_SWITCH_TO_DIFFERENT_PROGRAM_PIPELINE_OBJECT:
{
/* Switch to a different pipeline object and then back to the current one.
* Subroutine uniforms should be back at their default settings, instead of
* the ones we've just set.
*/
gl.bindProgramPipeline(
m_pipeline_object_ids[(n_object_id + 1) % 2 /* objects allocated for the test */]);
gl.bindProgramPipeline(m_pipeline_object_ids[n_object_id]);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindProgramPipeline() call(s) failed.");
break;
}
case TEST_CASE_SWITCH_TO_DIFFERENT_PIPELINE_FRAGMENT_STAGE:
{
/* Change the fragment shader stage to a different one.
*
* Note: We also need to update internal descriptor since the subroutine/uniform
* locations may be different between the two programs.
*/
gl.useProgramStages(m_pipeline_object_ids[n_object_id], GL_FRAGMENT_SHADER_BIT,
m_fs_po_ids[(n_object_id + 1) % 2 /* objects allocated for the test */]);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgramStages() call failed.");
cached_shader_stage_data = m_fs_po_descriptors[n_object_id];
m_fs_po_descriptors[n_object_id] = m_fs_po_descriptors[(n_object_id + 1) % 2];
stage_reset_status[Utils::SHADER_STAGE_FRAGMENT] = true;
uses_stage_reset_status = true;
break;
}
case TEST_CASE_SWITCH_TO_DIFFERENT_PIPELINE_GEOMETRY_STAGE:
{
/* Change the geometry shader stage to a different one.
*
* Note: We also need to update internal descriptor since the subroutine/uniform
* locations may be different between the two programs.
*/
gl.useProgramStages(m_pipeline_object_ids[n_object_id], GL_GEOMETRY_SHADER_BIT,
m_gs_po_ids[(n_object_id + 1) % 2 /* objects allocated for the test */]);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgramStages() call failed.");
cached_shader_stage_data = m_gs_po_descriptors[n_object_id];
m_gs_po_descriptors[n_object_id] = m_gs_po_descriptors[(n_object_id + 1) % 2];
stage_reset_status[Utils::SHADER_STAGE_GEOMETRY] = true;
uses_stage_reset_status = true;
break;
}
case TEST_CASE_SWITCH_TO_DIFFERENT_PIPELINE_TESS_CONTROL_STAGE:
{
/* Change the tessellation control shader stage to a different one.
*
* Note: We also need to update internal descriptor since the subroutine/uniform
* locations may be different between the two programs.
*/
gl.useProgramStages(m_pipeline_object_ids[n_object_id], GL_TESS_CONTROL_SHADER_BIT,
m_tc_po_ids[(n_object_id + 1) % 2 /* objects allocated for the test */]);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgramStages() call failed.");
cached_shader_stage_data = m_tc_po_descriptors[n_object_id];
m_tc_po_descriptors[n_object_id] = m_tc_po_descriptors[(n_object_id + 1) % 2];
stage_reset_status[Utils::SHADER_STAGE_TESSELLATION_CONTROL] = true;
uses_stage_reset_status = true;
break;
}
case TEST_CASE_SWITCH_TO_DIFFERENT_PIPELINE_TESS_EVALUATION_STAGE:
{
/* Change the tessellation evaluation shader stage to a different one.
*
* Note: We also need to update internal descriptor since the subroutine/uniform
* locations may be different between the two programs.
*/
gl.useProgramStages(m_pipeline_object_ids[n_object_id], GL_TESS_EVALUATION_SHADER_BIT,
m_te_po_ids[(n_object_id + 1) % 2 /* objects allocated for the test */]);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgramStages() call failed.");
cached_shader_stage_data = m_te_po_descriptors[n_object_id];
m_te_po_descriptors[n_object_id] = m_te_po_descriptors[(n_object_id + 1) % 2];
stage_reset_status[Utils::SHADER_STAGE_TESSELLATION_EVALUATION] = true;
uses_stage_reset_status = true;
break;
}
case TEST_CASE_SWITCH_TO_DIFFERENT_PIPELINE_VERTEX_STAGE:
{
/* Change the vertex shader stage to a different one.
*
* Note: We also need to update internal descriptor since the subroutine/uniform
* locations may be different between the two programs.
*/
gl.useProgramStages(m_pipeline_object_ids[n_object_id], GL_VERTEX_SHADER_BIT,
m_vs_po_ids[(n_object_id + 1) % 2 /* objects allocated for the test */]);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgramStages() call failed.");
cached_shader_stage_data = m_vs_po_descriptors[n_object_id];
m_vs_po_descriptors[n_object_id] = m_vs_po_descriptors[(n_object_id + 1) % 2];
stage_reset_status[Utils::SHADER_STAGE_VERTEX] = true;
uses_stage_reset_status = true;
break;
}
default:
{
TCU_FAIL("Unrecognized test case");
}
} /* switch (test_case) */
/* Verify the subroutine uniform values are valid */
if (!uses_stage_reset_status)
{
verifySubroutineUniformValues(static_cast<_test_case>(test_case), n_object_id,
SUBROUTINE_UNIFORMS_SET_TO_DEFAULT_VALUES);
}
else
{
const _shader_stage* shader_stages[Utils::SHADER_STAGE_COUNT] = { DE_NULL };
getShaderStages(static_cast<_test_case>(test_case) == TEST_CASE_SWITCH_TO_DIFFERENT_PROGRAM_OBJECT,
n_object_id, shader_stages);
for (unsigned int n_shader_stage = 0; n_shader_stage < Utils::SHADER_STAGE_COUNT; ++n_shader_stage)
{
const _shader_stage& current_shader_stage = *(shader_stages[n_shader_stage]);
if (stage_reset_status[n_shader_stage])
{
verifySubroutineUniformValuesForShaderStage(current_shader_stage,
SUBROUTINE_UNIFORMS_SET_TO_DEFAULT_VALUES);
}
else
{
verifySubroutineUniformValuesForShaderStage(current_shader_stage,
SUBROUTINE_UNIFORMS_SET_TO_NONDEFAULT_VALUES);
}
} /* for (all shader stages) */
}
/* Revert the changes some of the test cases appied */
switch (test_case)
{
case TEST_CASE_SWITCH_TO_DIFFERENT_PIPELINE_FRAGMENT_STAGE:
{
gl.useProgramStages(m_pipeline_object_ids[n_object_id], GL_FRAGMENT_SHADER_BIT,
m_fs_po_ids[n_object_id]);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgramStages() call failed.");
m_fs_po_descriptors[n_object_id] = cached_shader_stage_data;
break;
}
case TEST_CASE_SWITCH_TO_DIFFERENT_PIPELINE_GEOMETRY_STAGE:
{
gl.useProgramStages(m_pipeline_object_ids[n_object_id], GL_GEOMETRY_SHADER_BIT,
m_gs_po_ids[n_object_id]);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgramStages() call failed.");
m_gs_po_descriptors[n_object_id] = cached_shader_stage_data;
break;
}
case TEST_CASE_SWITCH_TO_DIFFERENT_PIPELINE_TESS_CONTROL_STAGE:
{
gl.useProgramStages(m_pipeline_object_ids[n_object_id], GL_TESS_CONTROL_SHADER_BIT,
m_tc_po_ids[n_object_id]);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgramStages() call failed.");
m_tc_po_descriptors[n_object_id] = cached_shader_stage_data;
break;
}
case TEST_CASE_SWITCH_TO_DIFFERENT_PIPELINE_TESS_EVALUATION_STAGE:
{
gl.useProgramStages(m_pipeline_object_ids[n_object_id], GL_TESS_EVALUATION_SHADER_BIT,
m_te_po_ids[n_object_id]);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgramStages() call failed.");
m_te_po_descriptors[n_object_id] = cached_shader_stage_data;
break;
}
case TEST_CASE_SWITCH_TO_DIFFERENT_PIPELINE_VERTEX_STAGE:
{
gl.useProgramStages(m_pipeline_object_ids[n_object_id], GL_VERTEX_SHADER_BIT, m_vs_po_ids[n_object_id]);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgramStages() call failed.");
m_vs_po_descriptors[n_object_id] = cached_shader_stage_data;
break;
}
default:
break;
} /* switch (test_case) */
} /* for (all program object descriptors) */
/* Unbind the program object */
gl.useProgram(0);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() call failed.");
} /* for (all test cases) */
if (m_has_test_passed)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
return STOP;
}
/** Verifies the subroutine uniform values reported by GL implementation. Depending on the test case,
* it will either query program object stages or separate shader objects.
*
* @param test_case Test case the verification is to be performed for.
* @param n_id Index of the program/pipeline object to use for the verification
* @param verification Verification method.
*/
void FunctionalTest16::verifySubroutineUniformValues(const _test_case& test_case, const unsigned int& n_id,
const _subroutine_uniform_value_verification& verification)
{
const _shader_stage* stages[] = {
DE_NULL, /* fragment shader stage slot */
DE_NULL, /* geometry shader stage slot */
DE_NULL, /* tess control shader stage slot */
DE_NULL, /* tess eval shader stage slot */
DE_NULL /* vertex shader stage slot */
};
const unsigned int n_stages = sizeof(stages) / sizeof(stages[0]);
/* Verify that currently reported subroutine uniform values are equal to default values */
getShaderStages(test_case == TEST_CASE_SWITCH_TO_DIFFERENT_PROGRAM_OBJECT, n_id, stages);
for (unsigned int n_stage = 0; n_stage < n_stages; ++n_stage)
{
const _shader_stage& current_stage = *(stages[n_stage]);
verifySubroutineUniformValuesForShaderStage(current_stage, verification);
} /* for (all items) */
}
/** Verifies the subroutine uniform values reported by GL implementation for user-specified
* shader stage. If the verification fails, m_has_test_passed will be set to false.
*
* @param shader_stage Descriptor of a shader stage that should be used for the process.
* @param verification Type of verification that should be performed.
*
**/
void FunctionalTest16::verifySubroutineUniformValuesForShaderStage(
const _shader_stage& shader_stage, const _subroutine_uniform_value_verification& verification)
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
glw::GLuint result_values[4] = { 0 };
gl.getUniformSubroutineuiv(shader_stage.gl_stage, shader_stage.subroutine1_uniform_location, result_values + 0);
gl.getUniformSubroutineuiv(shader_stage.gl_stage, shader_stage.subroutine2_uniform_location, result_values + 1);
gl.getUniformSubroutineuiv(shader_stage.gl_stage, shader_stage.subroutine3_uniform_location, result_values + 2);
gl.getUniformSubroutineuiv(shader_stage.gl_stage, shader_stage.subroutine4_uniform_location, result_values + 3);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetUniformSubroutineuiv() call(s) failed.");
if (verification == SUBROUTINE_UNIFORMS_SET_TO_VALID_VALUES)
{
if (!((result_values[0] == (GLuint)shader_stage.subroutine1_uniform_location ||
result_values[0] == (GLuint)shader_stage.subroutine2_uniform_location) &&
(result_values[1] == (GLuint)shader_stage.subroutine1_uniform_location ||
result_values[1] == (GLuint)shader_stage.subroutine2_uniform_location) &&
(result_values[2] == (GLuint)shader_stage.subroutine3_uniform_location ||
result_values[2] == (GLuint)shader_stage.subroutine4_uniform_location) &&
(result_values[3] == (GLuint)shader_stage.subroutine3_uniform_location ||
result_values[3] == (GLuint)shader_stage.subroutine4_uniform_location)))
{
m_testCtx.getLog() << tcu::TestLog::Message << "SUBROUTINE_UNIFORMS_SET_TO_VALID_VALUES validation failed. "
"Shader stage:["
<< Utils::getShaderStageStringFromGLEnum(shader_stage.gl_stage) << "], "
"expected data:["
<< shader_stage.subroutine1_uniform_location << " OR "
<< shader_stage.subroutine2_uniform_location << " x 2, "
<< shader_stage.subroutine3_uniform_location << " OR "
<< shader_stage.subroutine4_uniform_location << " x 2], "
"found data:["
<< result_values[0] << ", " << result_values[1] << ", " << result_values[2] << ", "
<< result_values[3] << "]." << tcu::TestLog::EndMessage;
m_has_test_passed = false;
}
}
else if (verification == SUBROUTINE_UNIFORMS_SET_TO_DEFAULT_VALUES)
{
if (result_values[0] != shader_stage.default_subroutine1_value ||
result_values[1] != shader_stage.default_subroutine2_value ||
result_values[2] != shader_stage.default_subroutine3_value ||
result_values[3] != shader_stage.default_subroutine4_value)
{
m_testCtx.getLog() << tcu::TestLog::Message
<< "SUBROUTINE_UNIFORMS_SET_TO_DEFAULT_VALUES validation failed. "
"Shader stage:["
<< Utils::getShaderStageStringFromGLEnum(shader_stage.gl_stage) << "], "
"expected data:["
<< shader_stage.default_subroutine1_value << ", "
<< shader_stage.default_subroutine2_value << ", "
<< shader_stage.default_subroutine3_value << ", "
<< shader_stage.default_subroutine4_value << "], "
"found data:["
<< result_values[0] << ", " << result_values[1] << ", " << result_values[2] << ", "
<< result_values[3] << "]." << tcu::TestLog::EndMessage;
m_has_test_passed = false;
}
}
else
{
DE_ASSERT(verification == SUBROUTINE_UNIFORMS_SET_TO_NONDEFAULT_VALUES);
if (result_values[0] == shader_stage.default_subroutine1_value ||
result_values[1] == shader_stage.default_subroutine2_value ||
result_values[2] == shader_stage.default_subroutine3_value ||
result_values[3] == shader_stage.default_subroutine4_value)
{
m_testCtx.getLog() << tcu::TestLog::Message
<< "SUBROUTINE_UNIFORMS_SET_TO_NONDEFAULT_VALUES validation failed. "
"Shader stage:["
<< Utils::getShaderStageStringFromGLEnum(shader_stage.gl_stage) << "], "
"expected data:!["
<< shader_stage.default_subroutine1_value << ", "
<< shader_stage.default_subroutine2_value << ", "
<< shader_stage.default_subroutine3_value << ", "
<< shader_stage.default_subroutine4_value << "], "
"found data:["
<< result_values[0] << ", " << result_values[1] << ", " << result_values[2] << ", "
<< result_values[3] << "]." << tcu::TestLog::EndMessage;
m_has_test_passed = false;
}
}
}
/** Constructor.
*
* @param context Rendering context.
*
**/
FunctionalTest17::FunctionalTest17(deqp::Context& context)
: TestCase(context, "same_subroutine_and_subroutine_uniform_but_different_type_used_in_all_stages",
"Creates a program which uses the same subroutine and subroutine uniform "
"names for every stage (types of subroutines are different in each stage) "
"and then makes sure that such program compiles and works as expected.")
, m_fbo_id(0)
, m_fs_id(0)
, m_gs_id(0)
, m_has_test_passed(true)
, m_po_id(0)
, m_tc_id(0)
, m_te_id(0)
, m_to_data(DE_NULL)
, m_to_height(4) /* arbitrary value */
, m_to_id(0)
, m_to_width(4) /* arbitrary value */
, m_vao_id(0)
, m_vs_id(0)
{
/* Left blank intentionally */
}
/** Deinitializes all GL objects that may have been created during test execution. */
void FunctionalTest17::deinit()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
if (m_fbo_id != 0)
{
gl.deleteFramebuffers(1, &m_fbo_id);
m_fbo_id = 0;
}
if (m_fs_id != 0)
{
gl.deleteShader(m_fs_id);
m_fs_id = 0;
}
if (m_gs_id != 0)
{
gl.deleteShader(m_gs_id);
m_gs_id = 0;
}
if (m_po_id != 0)
{
gl.deleteProgram(m_po_id);
m_po_id = 0;
}
if (m_tc_id != 0)
{
gl.deleteShader(m_tc_id);
m_tc_id = 0;
}
if (m_te_id != 0)
{
gl.deleteShader(m_te_id);
m_te_id = 0;
}
if (m_to_data != DE_NULL)
{
delete[] m_to_data;
m_to_data = DE_NULL;
}
if (m_to_id != 0)
{
gl.deleteTextures(1, &m_to_id);
m_to_id = 0;
}
if (m_vao_id != 0)
{
gl.deleteVertexArrays(1, &m_vao_id);
m_vao_id = 0;
}
if (m_vs_id != 0)
{
gl.deleteShader(m_vs_id);
m_vs_id = 0;
}
/* Restore original GL configuration */
gl.patchParameteri(GL_PATCH_VERTICES, 3);
GLU_EXPECT_NO_ERROR(gl.getError(), "glPatchParameteri() call failed.");
gl.pixelStorei(GL_PACK_ALIGNMENT, 4);
GLU_EXPECT_NO_ERROR(gl.getError(), "glPixelStorei() call failed.");
}
/** Retrieves body of a fragment shader that should be used by the test program.
*
* @return Requested string.
**/
std::string FunctionalTest17::getFragmentShaderBody() const
{
return "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"in GS_DATA\n"
"{\n"
" vec4 gs_data;\n"
" vec4 tc_data;\n"
" vec4 te_data;\n"
" vec4 vs_data;\n"
"} gs;\n"
"\n"
"out vec4 result;\n"
"\n"
"subroutine void subroutineTypeFS(out vec4 result);\n"
"\n"
"subroutine(subroutineTypeFS) void subroutine1(out vec4 result)\n"
"{\n"
" result = vec4(5, 6, 7, 8);\n"
"}\n"
"\n"
"subroutine uniform subroutineTypeFS function;\n"
"\n"
"void main()\n"
"{\n"
" vec4 fs_data;\n"
"\n"
" function(fs_data);\n"
" result = gs.gs_data + gs.tc_data + gs.te_data + gs.vs_data + fs_data;\n"
"}\n";
}
/** Retrieves body of a geometry shader that should be used by the test program.
*
* @return Requested string.
**/
std::string FunctionalTest17::getGeometryShaderBody() const
{
return "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"layout(points) in;\n"
"layout(triangle_strip, max_vertices = 4) out;\n"
"\n"
"subroutine void subroutineTypeGS(out vec4 result);\n"
"\n"
"subroutine(subroutineTypeGS) void subroutine1(out vec4 result)\n"
"{\n"
" result = vec4(4, 5, 6, 7);\n"
"}\n"
"\n"
"subroutine uniform subroutineTypeGS function;\n"
"\n"
"in TE_DATA\n"
"{\n"
" vec4 tc_data;\n"
" vec4 te_data;\n"
" vec4 vs_data;\n"
"} te[];\n"
"\n"
"out GS_DATA\n"
"{\n"
" vec4 gs_data;\n"
" vec4 tc_data;\n"
" vec4 te_data;\n"
" vec4 vs_data;\n"
"} result;\n"
"\n"
"void main()\n"
"{\n"
" function(result.gs_data);\n"
" gl_Position = vec4(1, -1, 0, 1);\n"
" result.tc_data = te[0].tc_data;\n"
" result.te_data = te[0].te_data;\n"
" result.vs_data = te[0].vs_data;\n"
" EmitVertex();\n"
"\n"
" function(result.gs_data);\n"
" gl_Position = vec4(-1, -1, 0, 1);\n"
" result.tc_data = te[0].tc_data;\n"
" result.te_data = te[0].te_data;\n"
" result.vs_data = te[0].vs_data;\n"
" EmitVertex();\n"
"\n"
" function(result.gs_data);\n"
" gl_Position = vec4(1, 1, 0, 1);\n"
" result.tc_data = te[0].tc_data;\n"
" result.te_data = te[0].te_data;\n"
" result.vs_data = te[0].vs_data;\n"
" EmitVertex();\n"
"\n"
" function(result.gs_data);\n"
" gl_Position = vec4(-1, 1, 0, 1);\n"
" result.tc_data = te[0].tc_data;\n"
" result.te_data = te[0].te_data;\n"
" result.vs_data = te[0].vs_data;\n"
" EmitVertex();\n"
" EndPrimitive();\n"
"}\n";
}
/** Retrieves body of a tessellation control shader that should be used by the test program.
*
* @return Requested string.
**/
std::string FunctionalTest17::getTessellationControlShaderBody() const
{
return "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"layout (vertices = 4) out;\n"
"\n"
"subroutine void subroutineTypeTC(out vec4 result);\n"
"\n"
"subroutine(subroutineTypeTC) void subroutine1(out vec4 result)\n"
"{\n"
" result = vec4(2, 3, 4, 5);\n"
"}\n"
"\n"
"subroutine uniform subroutineTypeTC function;\n"
"\n"
"in VS_DATA\n"
"{\n"
" vec4 vs_data;\n"
"} vs[];\n"
"\n"
"out TC_DATA\n"
"{\n"
" vec4 tc_data;\n"
" vec4 vs_data;\n"
"} result[];\n"
"\n"
"void main()\n"
"{\n"
" gl_TessLevelInner[0] = 1.0;\n"
" gl_TessLevelInner[1] = 1.0;\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"
"\n"
" function(result[gl_InvocationID].tc_data);\n"
" result[gl_InvocationID].vs_data = vs[gl_InvocationID].vs_data;\n"
"}\n";
}
/** Retrieves body of a tessellation evaluation shader that should be used
* by the test program.
*
* @return Requested string.
**/
std::string FunctionalTest17::getTessellationEvaluationShaderBody() const
{
return "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"layout (quads, point_mode) in;\n"
"\n"
"subroutine void subroutineTypeTE(out vec4 result);\n"
"\n"
"subroutine(subroutineTypeTE) void subroutine1(out vec4 result)\n"
"{\n"
" result = vec4(3, 4, 5, 6);\n"
"}\n"
"\n"
"subroutine uniform subroutineTypeTE function;\n"
"\n"
"in TC_DATA\n"
"{\n"
" vec4 tc_data;\n"
" vec4 vs_data;\n"
"} tc[];\n"
"\n"
"out TE_DATA\n"
"{\n"
" vec4 tc_data;\n"
" vec4 te_data;\n"
" vec4 vs_data;\n"
"} result;\n"
"\n"
"void main()\n"
"{\n"
" result.vs_data = tc[0].vs_data;\n"
" result.tc_data = tc[0].tc_data;\n"
" function(result.te_data);\n"
"}\n";
}
/** Retrieves body of a vertex shader that should be used by the test program.
*
* @return Requested string.
**/
std::string FunctionalTest17::getVertexShaderBody() const
{
return "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"out VS_DATA\n"
"{\n"
" vec4 vs_data;\n"
"} result;\n"
"\n"
"subroutine void subroutineTypeVS(out vec4 result);\n"
"\n"
"subroutine(subroutineTypeVS) void subroutine1(out vec4 result)\n"
"{\n"
" result = vec4(1, 2, 3, 4);\n"
"}\n"
"\n"
"subroutine uniform subroutineTypeVS function;\n"
"\n"
"void main()\n"
"{\n"
" function(result.vs_data);\n"
"}\n";
}
/** Initializes all buffers and GL objects required to run the test. */
void FunctionalTest17::initTest()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Configure GL_PATCH_VERTICES so that TC only takes a single patch vertex */
gl.patchParameteri(GL_PATCH_VERTICES, 1);
GLU_EXPECT_NO_ERROR(gl.getError(), "glPatchParameteri() call failed.");
/* Generate & bind a VAO */
gl.genVertexArrays(1, &m_vao_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenVertexArrays() call failed.");
gl.bindVertexArray(m_vao_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray() call failed.");
/* Set up test program object */
std::string fs_body = getFragmentShaderBody();
std::string gs_body = getGeometryShaderBody();
std::string tc_body = getTessellationControlShaderBody();
std::string te_body = getTessellationEvaluationShaderBody();
std::string vs_body = getVertexShaderBody();
if (!Utils::buildProgram(gl, vs_body, tc_body, te_body, gs_body, fs_body, DE_NULL, /* xfb_varyings */
DE_NULL, /* n_xfb_varyings */
&m_vs_id, &m_tc_id, &m_te_id, &m_gs_id, &m_fs_id, &m_po_id))
{
TCU_FAIL("Failed to link test program object");
}
/* Set up a texture object that will be used as a color attachment */
gl.genTextures(1, &m_to_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenTextures() call failed.");
gl.bindTexture(GL_TEXTURE_2D, m_to_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTexture() call failed.");
gl.texStorage2D(GL_TEXTURE_2D, 1, /* levels */
GL_RGBA32F, m_to_width, m_to_height);
GLU_EXPECT_NO_ERROR(gl.getError(), "glTexStorage2D() call failed.");
/* Set up FBO */
gl.genFramebuffers(1, &m_fbo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenFramebuffers() call failed.");
gl.bindFramebuffer(GL_FRAMEBUFFER, m_fbo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer() call failed.");
gl.framebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_to_id, 0 /* level */);
GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferTexture2D() call failed.");
/* Make sure glReadPixels() does not return misaligned data */
gl.pixelStorei(GL_PACK_ALIGNMENT, 1);
GLU_EXPECT_NO_ERROR(gl.getError(), "glPixelStorei() call failed.");
/* Initialize a buffer that will be used to store rendered data */
m_to_data = new float[m_to_width * m_to_height * 4 /* rgba */];
}
/** Executes test iteration.
*
* @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed.
*/
tcu::TestNode::IterateResult FunctionalTest17::iterate()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Do not execute the test if GL_ARB_shader_subroutine is not supported */
if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_shader_subroutine"))
{
throw tcu::NotSupportedError("GL_ARB_shader_subroutine is not supported.");
}
initTest();
/* Use the test program to render a full-screen test quad */
gl.useProgram(m_po_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() call failed.");
gl.drawArrays(GL_PATCHES, 0 /* first */, 1 /* count */);
GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays() call failed.");
/* Read back the data that was rendered */
gl.readPixels(0, /* x */
0, /* y */
m_to_width, m_to_height, GL_RGBA, GL_FLOAT, m_to_data);
GLU_EXPECT_NO_ERROR(gl.getError(), "glReaDPixels() call failed.");
/* Verify the data */
verifyRenderedData();
/** All done */
if (m_has_test_passed)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
return STOP;
}
/** Verifies the data that have been rendered by the test program.
*
* It is assumed the rendered data have already been copied to
* m_to_data.
*
* If the rendered data is found to be invalid, m_has_test_passed
* will be set to false.
**/
void FunctionalTest17::verifyRenderedData()
{
const float epsilon = 1e-5f;
const float expected_data[4] = { 15.0f, 20.0f, 25.0f, 30.0f };
for (unsigned int y = 0; y < m_to_height && m_has_test_passed; ++y)
{
const float* row_ptr = m_to_data + y * 4 /* rgba */ * m_to_width;
for (unsigned int x = 0; x < m_to_width && m_has_test_passed; ++x)
{
const float* pixel_ptr = row_ptr + 4 /* rgba */ * x;
if (de::abs(pixel_ptr[0] - expected_data[0]) > epsilon ||
de::abs(pixel_ptr[1] - expected_data[1]) > epsilon ||
de::abs(pixel_ptr[2] - expected_data[2]) > epsilon ||
de::abs(pixel_ptr[3] - expected_data[3]) > epsilon)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Invalid texel found at (" << x << ", " << y
<< "): "
"expected:("
<< expected_data[0] << ", " << expected_data[1] << ", " << expected_data[2] << ", "
<< expected_data[3] << "), found:(" << pixel_ptr[0] << ", " << pixel_ptr[1] << ", "
<< pixel_ptr[2] << ", " << pixel_ptr[3] << ")." << tcu::TestLog::EndMessage;
m_has_test_passed = false;
}
} /* for (all columns) */
} /* for (all rows) */
}
/** Constructor.
*
* @param context Rendering context.
*
**/
FunctionalTest18_19::FunctionalTest18_19(deqp::Context& context)
: TestCase(context, "control_flow_and_returned_subroutine_values_used_as_subroutine_input",
"Makes sure that calling a subroutine with argument value returned by "
"another subroutine works correctly. Also checks that subroutine and "
"subroutine uniforms work as expected when used in connection with control "
"flow functions.")
, m_has_test_passed(true)
, m_n_points_to_draw(16) /* arbitrary value */
, m_po_id(0)
, m_po_subroutine_divide_by_two_location(GL_INVALID_INDEX)
, m_po_subroutine_multiply_by_four_location(GL_INVALID_INDEX)
, m_po_subroutine_returns_false_location(GL_INVALID_INDEX)
, m_po_subroutine_returns_true_location(GL_INVALID_INDEX)
, m_po_subroutine_uniform_bool_operator1(-1)
, m_po_subroutine_uniform_bool_operator2(-1)
, m_po_subroutine_uniform_vec4_processor1(-1)
, m_po_subroutine_uniform_vec4_processor2(-1)
, m_xfb_bo_id(0)
, m_vao_id(0)
, m_vs_id(0)
{
/* Left blank intentionally */
}
/** De-initializes all GL objects that may have been created during test execution */
void FunctionalTest18_19::deinit()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
if (m_po_id != 0)
{
gl.deleteProgram(m_po_id);
m_po_id = 0;
}
if (m_vao_id != 0)
{
gl.deleteVertexArrays(1, &m_vao_id);
m_vao_id = 0;
}
if (m_vs_id != 0)
{
gl.deleteShader(m_vs_id);
m_vs_id = 0;
}
if (m_xfb_bo_id != 0)
{
gl.deleteBuffers(1, &m_xfb_bo_id);
m_xfb_bo_id = 0;
}
}
/** Executes a single test iteration using user-specified properties. If the
* iterations fails, m_has_test_passed is set to false.
*
* @param bool_operator1_subroutine_location Location of a subroutine to be assigned to
* bool_operator1 subroutine uniform.
* @param bool_operator2_subroutine_location Location of a subroutine to be assigned to
* bool_operator2 subroutine uniform.
* @param vec4_operator1_subroutine_location Location of a subroutine to be assigned to
* vec4_operator1 subroutine uniform.
* @param vec4_operator2_subroutine_location Location of a subroutine to be assigned to
* vec4_operator2 subroutine uniform.
&**/
void FunctionalTest18_19::executeTest(glw::GLuint bool_operator1_subroutine_location,
glw::GLuint bool_operator2_subroutine_location,
glw::GLuint vec4_operator1_subroutine_location,
glw::GLuint vec4_operator2_subroutine_location)
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Set up subroutines */
glw::GLuint subroutine_configuration[4 /* total number of subroutines */] = { 0 };
subroutine_configuration[m_po_subroutine_uniform_bool_operator1] = bool_operator1_subroutine_location;
subroutine_configuration[m_po_subroutine_uniform_bool_operator2] = bool_operator2_subroutine_location;
subroutine_configuration[m_po_subroutine_uniform_vec4_processor1] = vec4_operator1_subroutine_location;
subroutine_configuration[m_po_subroutine_uniform_vec4_processor2] = vec4_operator2_subroutine_location;
gl.useProgram(m_po_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() call failed");
gl.uniformSubroutinesuiv(GL_VERTEX_SHADER, 4 /* count */, subroutine_configuration);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUniformSubroutinesuiv() call failed");
/* Draw test-specific number of points */
gl.beginTransformFeedback(GL_POINTS);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback() call failed");
{
gl.drawArrays(GL_POINTS, 0 /* first */, m_n_points_to_draw);
GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays() call failed");
}
gl.endTransformFeedback();
GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback() call failed");
/* Map the BO storage into process space */
const glw::GLvoid* xfb_data_ptr = gl.mapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, GL_READ_ONLY);
GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBuffer() call failed.");
verifyXFBData(xfb_data_ptr, bool_operator1_subroutine_location, bool_operator2_subroutine_location,
vec4_operator1_subroutine_location, vec4_operator2_subroutine_location);
/* Unmap BO storage */
gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer() call failed.");
}
/** Retrieves body of a vertex shader to be used by the test. */
std::string FunctionalTest18_19::getVertexShaderBody() const
{
return "#version 400\n"
"\n"
"subroutine bool bool_processor();\n"
"subroutine vec4 vec4_processor(in vec4 iparam);\n"
"\n"
"subroutine(bool_processor) bool returnsFalse()\n"
"{\n"
" return false;\n"
"}\n"
"\n"
"subroutine(bool_processor) bool returnsTrue()\n"
"{\n"
" return true;\n"
"}\n"
"\n"
"subroutine(vec4_processor) vec4 divideByTwo(in vec4 iparam)\n"
"{\n"
" return iparam * vec4(0.5);\n"
"}\n"
"\n"
"subroutine(vec4_processor) vec4 multiplyByFour(in vec4 iparam)\n"
"{\n"
" return iparam * vec4(4.0);\n"
"}\n"
"\n"
"subroutine uniform bool_processor bool_operator1;\n"
"subroutine uniform bool_processor bool_operator2;\n"
"subroutine uniform vec4_processor vec4_operator1;\n"
"subroutine uniform vec4_processor vec4_operator2;\n"
"\n"
"out float result;\n"
"\n"
"void main()\n"
"{\n"
" if (bool_operator1() )\n"
" {\n"
" float value = float( (3 * gl_VertexID + 1) * 2);\n"
"\n"
" while (bool_operator1() )\n"
" {\n"
" value /= float(gl_VertexID + 2);\n"
"\n"
" if (value <= 1.0f) break;\n"
" }\n"
"\n"
" result = value;\n"
" }\n"
" else\n"
" {\n"
" vec4 value = vec4(gl_VertexID, gl_VertexID + 1,\n"
" gl_VertexID + 2, gl_VertexID + 3);\n"
"\n"
" switch (gl_VertexID % 2)\n"
" {\n"
" case 0:\n"
" {\n"
" for (int iteration = 0; iteration < gl_VertexID && bool_operator2(); ++iteration)\n"
" {\n"
" value = vec4_operator2(vec4_operator1(value));\n"
" }\n"
"\n"
" break;\n"
" }\n"
"\n"
" case 1:\n"
" {\n"
" for (int iteration = 0; iteration < gl_VertexID * 2; ++iteration)\n"
" {\n"
" value = vec4_operator1(vec4_operator2(value));\n"
" }\n"
"\n"
" break;\n"
" }\n"
" }\n"
"\n"
" result = value.x + value.y + value.z + value.w;\n"
"\n"
" }\n"
"}\n";
}
/** Initializes all GL objects required to run the test. */
void FunctionalTest18_19::initTest()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
const char* varyings[1] = { "result" };
std::string vs_body = getVertexShaderBody();
const unsigned int n_varyings = sizeof(varyings) / sizeof(varyings[0]);
if (!Utils::buildProgram(gl, vs_body, "", /* tc_body */
"", /* te_body */
"", /* gs_body */
"", /* fs_body */
varyings, n_varyings, &m_vs_id, DE_NULL, /* out_tc_id */
DE_NULL, /* out_te_id */
DE_NULL, /* out_gs_id */
DE_NULL, /* out_fs_id */
&m_po_id))
{
TCU_FAIL("Failed to build test program object");
}
/* Retrieve subroutine & subroutine uniform locations */
m_po_subroutine_divide_by_two_location = gl.getSubroutineIndex(m_po_id, GL_VERTEX_SHADER, "divideByTwo");
m_po_subroutine_multiply_by_four_location = gl.getSubroutineIndex(m_po_id, GL_VERTEX_SHADER, "multiplyByFour");
m_po_subroutine_returns_false_location = gl.getSubroutineIndex(m_po_id, GL_VERTEX_SHADER, "returnsFalse");
m_po_subroutine_returns_true_location = gl.getSubroutineIndex(m_po_id, GL_VERTEX_SHADER, "returnsTrue");
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetSubroutineIndex() call(s) failed");
if (m_po_subroutine_divide_by_two_location == GL_INVALID_INDEX ||
m_po_subroutine_multiply_by_four_location == GL_INVALID_INDEX ||
m_po_subroutine_returns_false_location == GL_INVALID_INDEX ||
m_po_subroutine_returns_true_location == GL_INVALID_INDEX)
{
TCU_FAIL("glGetSubroutineIndex() returned GL_INVALID_INDEX for a valid subroutine");
}
m_po_subroutine_uniform_bool_operator1 =
gl.getSubroutineUniformLocation(m_po_id, GL_VERTEX_SHADER, "bool_operator1");
m_po_subroutine_uniform_bool_operator2 =
gl.getSubroutineUniformLocation(m_po_id, GL_VERTEX_SHADER, "bool_operator2");
m_po_subroutine_uniform_vec4_processor1 =
gl.getSubroutineUniformLocation(m_po_id, GL_VERTEX_SHADER, "vec4_operator1");
m_po_subroutine_uniform_vec4_processor2 =
gl.getSubroutineUniformLocation(m_po_id, GL_VERTEX_SHADER, "vec4_operator2");
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetSubroutineUniformLocation() call(s) failed");
if (m_po_subroutine_uniform_bool_operator1 == -1 || m_po_subroutine_uniform_bool_operator2 == -1 ||
m_po_subroutine_uniform_vec4_processor1 == -1 || m_po_subroutine_uniform_vec4_processor2 == -1)
{
TCU_FAIL("glGetSubroutineUniformLocation() returned -1 for an active subroutine uniform");
}
/* Set up XFB BO */
const unsigned int bo_size = static_cast<unsigned int>(sizeof(float) * m_n_points_to_draw);
gl.genBuffers(1, &m_xfb_bo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers() call failed.");
gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_xfb_bo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call failed.");
gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* index */, m_xfb_bo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase() call failed.");
gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, bo_size, DE_NULL /* data */, GL_STATIC_COPY);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() call failed.");
/* Set up a VAO */
gl.genVertexArrays(1, &m_vao_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenVertexArrays() call failed.");
gl.bindVertexArray(m_vao_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray() call failed.");
}
/** Executes test iteration.
*
* @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed.
*/
tcu::TestNode::IterateResult FunctionalTest18_19::iterate()
{
/* Do not execute the test if GL_ARB_shader_subroutine is not supported */
if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_shader_subroutine"))
{
throw tcu::NotSupportedError("GL_ARB_shader_subroutine is not supported.");
}
/* Initialize all GL objects required to run the test */
initTest();
/* Iterate over all subroutine permutations */
const glw::GLuint subroutine_bool_operators[] = { m_po_subroutine_returns_false_location,
m_po_subroutine_returns_true_location };
const unsigned int n_subroutine_bool_operators =
sizeof(subroutine_bool_operators) / sizeof(subroutine_bool_operators[0]);
const glw::GLuint subroutine_vec4_operators[] = { m_po_subroutine_divide_by_two_location,
m_po_subroutine_multiply_by_four_location };
const unsigned int n_subroutine_vec4_operators =
sizeof(subroutine_vec4_operators) / sizeof(subroutine_vec4_operators[0]);
for (unsigned int n_subroutine_uniform_bool_operator1 = 0;
n_subroutine_uniform_bool_operator1 < n_subroutine_bool_operators; ++n_subroutine_uniform_bool_operator1)
{
for (unsigned int n_subroutine_uniform_bool_operator2 = 0;
n_subroutine_uniform_bool_operator2 < n_subroutine_bool_operators; ++n_subroutine_uniform_bool_operator2)
{
for (unsigned int n_subroutine_uniform_vec4_operator1 = 0;
n_subroutine_uniform_vec4_operator1 < n_subroutine_vec4_operators;
++n_subroutine_uniform_vec4_operator1)
{
for (unsigned int n_subroutine_uniform_vec4_operator2 = 0;
n_subroutine_uniform_vec4_operator2 < n_subroutine_vec4_operators;
++n_subroutine_uniform_vec4_operator2)
{
executeTest(subroutine_bool_operators[n_subroutine_uniform_bool_operator1],
subroutine_bool_operators[n_subroutine_uniform_bool_operator2],
subroutine_vec4_operators[n_subroutine_uniform_vec4_operator1],
subroutine_vec4_operators[n_subroutine_uniform_vec4_operator2]);
} /* for (all subroutine vec4 operator subroutines used for processor2) */
} /* for (all subroutine vec4 operator subroutines used for processor1) */
} /* for (all subroutine bool operator subroutines used for operator2) */
} /* for (all subroutine bool operator subroutines used for operator1) */
/* All done */
if (m_has_test_passed)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
return STOP;
}
/** Divides input argument by two. The result value is returned to the
* caller.
*
* @param data Input value.
*
* @return As per description.
**/
tcu::Vec4 FunctionalTest18_19::vec4operator_div2(tcu::Vec4 data)
{
return data * 0.5f;
}
/** Multiplies input argument by four. The result value is returned to the
* caller.
*
* @param data Input value.
*
* @return As per description.
**/
tcu::Vec4 FunctionalTest18_19::vec4operator_mul4(tcu::Vec4 data)
{
return data * 4.0f;
}
/** Verifies data XFBed out by the vertex shader. It is assumed the subroutines were configured
* as per passed arguments, prior to the draw call.
*
* If the result data is found to be invalid, m_has_test_passed is set to false.
*
* @param data XFBed data.
* @param bool_operator1_subroutine_location Location of a subroutine to be assigned to
* bool_operator1 subroutine uniform.
* @param bool_operator2_subroutine_location Location of a subroutine to be assigned to
* bool_operator2 subroutine uniform.
* @param vec4_operator1_subroutine_location Location of a subroutine to be assigned to
* vec4_operator1 subroutine uniform.
* @param vec4_operator2_subroutine_location Location of a subroutine to be assigned to
* vec4_operator2 subroutine uniform.
*/
void FunctionalTest18_19::verifyXFBData(const glw::GLvoid* data, glw::GLuint bool_operator1_subroutine_location,
glw::GLuint bool_operator2_subroutine_location,
glw::GLuint vec4_operator1_subroutine_location,
glw::GLuint vec4_operator2_subroutine_location)
{
bool bool_operator1_result = false;
bool bool_operator2_result = false;
const float epsilon = 1e-5f;
PFNVEC4OPERATORPROC pVec4Operator1 = NULL;
PFNVEC4OPERATORPROC pVec4Operator2 = NULL;
const glw::GLfloat* traveller_ptr = (const glw::GLfloat*)data;
bool_operator1_result = (bool_operator1_subroutine_location == m_po_subroutine_returns_true_location);
bool_operator2_result = (bool_operator2_subroutine_location == m_po_subroutine_returns_true_location);
pVec4Operator1 = (vec4_operator1_subroutine_location == m_po_subroutine_divide_by_two_location) ?
vec4operator_div2 :
vec4operator_mul4;
pVec4Operator2 = (vec4_operator2_subroutine_location == m_po_subroutine_divide_by_two_location) ?
vec4operator_div2 :
vec4operator_mul4;
for (unsigned int n_vertex = 0; n_vertex < m_n_points_to_draw; ++n_vertex)
{
float expected_value = 0.0f;
if (bool_operator1_result)
{
float value = float((3 * n_vertex + 1) * 2);
while (bool_operator1_result)
{
value /= float(n_vertex + 2);
if (value <= 1.0f)
break;
}
expected_value = value;
}
else
{
tcu::Vec4 value((float)n_vertex, (float)n_vertex + 1, (float)n_vertex + 2, (float)n_vertex + 3);
switch (n_vertex % 2)
{
case 0:
{
for (unsigned int iteration = 0; iteration < n_vertex && bool_operator2_result; ++iteration)
{
value = pVec4Operator2(pVec4Operator1(value));
}
break;
}
case 1:
{
for (unsigned int iteration = 0; iteration < n_vertex * 2; ++iteration)
{
value = pVec4Operator1(pVec4Operator2(value));
}
break;
}
} /* switch (n_vertex % 2) */
expected_value = value.x() + value.y() + value.z() + value.w();
}
if (de::abs(expected_value - *traveller_ptr) > epsilon)
{
m_testCtx.getLog() << tcu::TestLog::Message << "XFBed data was found to be invalid at index [" << n_vertex
<< "]"
"for the following subroutine location configuration:"
" bool_operator1_subroutine_location:["
<< bool_operator1_subroutine_location << "]"
" bool_operator2_subroutine_location:["
<< bool_operator2_subroutine_location << "]"
" vec4_operator1_subroutine_location:["
<< vec4_operator1_subroutine_location << "]"
" vec4_operator2_subroutine_location:["
<< vec4_operator2_subroutine_location << "];"
" expected data:"
<< expected_value << ", found:" << *traveller_ptr << tcu::TestLog::EndMessage;
m_has_test_passed = false;
}
++traveller_ptr;
} /* for (all drawn points) */
}
/** Constructor.
*
* @param context Rendering context.
*
**/
NegativeTest1::NegativeTest1(deqp::Context& context)
: TestCase(context, "subroutine_errors", "Verifies all GL_INVALID_OPERATION, GL_INVALID_VALUE, GL_INVALID ENUM "
"errors related to subroutine usage are properly generated.")
, m_has_test_passed(true)
, m_po_active_subroutine_uniform_locations(0)
, m_po_active_subroutine_uniforms(0)
, m_po_active_subroutines(0)
, m_po_subroutine_uniform_function_index(-1)
, m_po_subroutine_uniform_function2_index(-1)
, m_po_subroutine_test1_index(GL_INVALID_INDEX)
, m_po_subroutine_test2_index(GL_INVALID_INDEX)
, m_po_subroutine_test3_index(GL_INVALID_INDEX)
, m_po_not_linked_id(0)
, m_po_id(0)
, m_vs_id(0)
{
/* Left blank intentionally */
}
/** Deinitializes all GL objects that may have been created during
* test execution.
**/
void NegativeTest1::deinit()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
if (m_po_id != 0)
{
gl.deleteProgram(m_po_id);
m_po_id = 0;
}
if (m_po_not_linked_id != 0)
{
gl.deleteProgram(m_po_not_linked_id);
m_po_not_linked_id = 0;
}
if (m_vs_id != 0)
{
gl.deleteShader(m_vs_id);
m_vs_id = 0;
}
}
/** Initializes all GL objects required to run the test. */
void NegativeTest1::initTest()
{
glw::GLint compile_status = GL_FALSE;
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Create program objects */
m_po_not_linked_id = gl.createProgram();
m_po_id = gl.createProgram();
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() call(s) failed.");
/* Create vertex shader object */
m_vs_id = gl.createShader(GL_VERTEX_SHADER);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() call failed.");
/* Set up vertex shader */
const char* vs_body = "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"subroutine void subroutineType (out ivec2 arg);\n"
"subroutine void subroutineType2(out ivec4 arg);\n"
"\n"
"subroutine(subroutineType) void test1(out ivec2 arg)\n"
"{\n"
" arg = ivec2(1, 2);\n"
"}\n"
"subroutine(subroutineType) void test2(out ivec2 arg)\n"
"{\n"
" arg = ivec2(3,4);\n"
"}\n"
"subroutine(subroutineType2) void test3(out ivec4 arg)\n"
"{\n"
" arg = ivec4(1, 2, 3, 4);\n"
"}\n"
"\n"
"subroutine uniform subroutineType function;\n"
"subroutine uniform subroutineType2 function2;\n"
"\n"
"void main()\n"
"{\n"
" ivec2 test;\n"
" ivec4 test2;\n"
"\n"
" function(test);\n"
"\n"
" if (test.x > 2)\n"
" {\n"
" gl_Position = vec4(1);\n"
" }\n"
" else\n"
" {\n"
" function2(test2);\n"
"\n"
" gl_Position = vec4(float(test2.x) );\n"
" }\n"
"}\n";
gl.shaderSource(m_vs_id, 1 /* count */, &vs_body, DE_NULL /* length */);
GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() call failed");
gl.compileShader(m_vs_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader() call failed");
gl.getShaderiv(m_vs_id, GL_COMPILE_STATUS, &compile_status);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv() call failed");
if (compile_status == GL_FALSE)
{
TCU_FAIL("Shader compilation failed");
}
/* Set up & link the test program object */
glw::GLint link_status = GL_FALSE;
gl.attachShader(m_po_id, m_vs_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader() call failed.");
gl.linkProgram(m_po_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glLinkProgram() call failed.");
gl.getProgramiv(m_po_id, GL_LINK_STATUS, &link_status);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv() call failed.");
if (link_status == GL_FALSE)
{
TCU_FAIL("Program linking failed");
}
/* Query test program object's properties */
gl.getProgramStageiv(m_po_id, GL_VERTEX_SHADER, GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS,
&m_po_active_subroutine_uniform_locations);
gl.getProgramStageiv(m_po_id, GL_VERTEX_SHADER, GL_ACTIVE_SUBROUTINE_UNIFORMS, &m_po_active_subroutine_uniforms);
gl.getProgramStageiv(m_po_id, GL_VERTEX_SHADER, GL_ACTIVE_SUBROUTINES, &m_po_active_subroutines);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramStageiv() call(s) failed.");
if (m_po_active_subroutine_uniform_locations != 2)
{
TCU_FAIL("Invalid GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS value returned");
}
m_po_subroutine_test1_index = gl.getSubroutineIndex(m_po_id, GL_VERTEX_SHADER, "test1");
m_po_subroutine_test2_index = gl.getSubroutineIndex(m_po_id, GL_VERTEX_SHADER, "test2");
m_po_subroutine_test3_index = gl.getSubroutineIndex(m_po_id, GL_VERTEX_SHADER, "test3");
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetSubroutineIndex() call(s) failed.");
if (m_po_subroutine_test1_index == GL_INVALID_INDEX || m_po_subroutine_test2_index == GL_INVALID_INDEX ||
m_po_subroutine_test3_index == GL_INVALID_INDEX)
{
TCU_FAIL("Invalid subroutine index returned");
}
m_po_subroutine_uniform_function_index = gl.getSubroutineUniformLocation(m_po_id, GL_VERTEX_SHADER, "function");
m_po_subroutine_uniform_function2_index = gl.getSubroutineUniformLocation(m_po_id, GL_VERTEX_SHADER, "function2");
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetSubroutineUniformLocation() call(s) failed.");
if (m_po_subroutine_uniform_function_index == -1 || m_po_subroutine_uniform_function2_index == -1)
{
TCU_FAIL("Invalid subroutine uniform index returned");
}
}
/** Executes test iteration.
*
* @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed.
*/
tcu::TestNode::IterateResult NegativeTest1::iterate()
{
glw::GLenum error_code = GL_NO_ERROR;
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Do not execute the test if GL_ARB_shader_subroutine is not supported */
if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_shader_subroutine"))
{
throw tcu::NotSupportedError("GL_ARB_shader_subroutine is not supported.");
}
/* Initialize GL objects required to run the test */
initTest();
/* The error INVALID_OPERATION is generated by GetSubroutineUniformLocation
* if the program object identified by <program> has not been successfully
* linked.
*/
gl.getSubroutineUniformLocation(m_po_not_linked_id, GL_FRAGMENT_SHADER, "subroutine_uniform_name");
error_code = gl.getError();
if (error_code != GL_INVALID_OPERATION)
{
m_testCtx.getLog() << tcu::TestLog::Message
<< "glGetSubroutineUniformLocation() does not generate GL_INVALID_OPERATION "
"error code when called for a non-linked program object."
<< tcu::TestLog::EndMessage;
m_has_test_passed = false;
}
/* The error INVALID_VALUE is generated by GetActiveSubroutineUniformiv or
* GetActiveSubroutineUniformName if <index> is greater than or equal to the
* value of ACTIVE_SUBROUTINE_UNIFORMS for the shader stage.
*/
glw::GLint temp_length = 0;
glw::GLint temp_values = 0;
gl.getActiveSubroutineUniformiv(m_po_id, GL_VERTEX_SHADER, m_po_active_subroutine_uniforms,
GL_NUM_COMPATIBLE_SUBROUTINES, &temp_values);
error_code = gl.getError();
if (error_code == GL_INVALID_VALUE)
{
gl.getActiveSubroutineUniformiv(m_po_id, GL_VERTEX_SHADER, m_po_active_subroutine_uniforms + 1,
GL_NUM_COMPATIBLE_SUBROUTINES, &temp_values);
error_code = gl.getError();
}
if (error_code != GL_INVALID_VALUE)
{
m_testCtx.getLog() << tcu::TestLog::Message
<< "glGetActiveSubroutineUniformiv() does not generate GL_INVALID_VALUE "
"when passed <index> argument that is greater than or equal to "
"the value of GL_ACTIVE_SUBROUTINE_UNIFORMS."
<< tcu::TestLog::EndMessage;
m_has_test_passed = false;
}
gl.getActiveSubroutineUniformName(m_po_id, GL_VERTEX_SHADER, m_po_active_subroutine_uniforms, 0, /* bufsize */
&temp_length, DE_NULL); /* name */
error_code = gl.getError();
if (error_code == GL_INVALID_VALUE)
{
gl.getActiveSubroutineUniformName(m_po_id, GL_VERTEX_SHADER, m_po_active_subroutine_uniforms + 1,
0, /* bufsize */
&temp_length, DE_NULL); /* name */
error_code = gl.getError();
}
if (error_code != GL_INVALID_VALUE)
{
m_testCtx.getLog() << tcu::TestLog::Message
<< "glGetActiveSubroutineUniformName() does not generate GL_INVALID_VALUE "
"when passed <index> argument that is greater than or equal to "
"the value of GL_ACTIVE_SUBROUTINE_UNIFORMS."
<< tcu::TestLog::EndMessage;
m_has_test_passed = false;
}
/* The error INVALID_VALUE is generated by GetActiveSubroutineName if <index>
* is greater than or equal to the value of ACTIVE_SUBROUTINES for the shader
* stage.
*/
gl.getActiveSubroutineName(m_po_id, GL_VERTEX_SHADER, m_po_active_subroutines, 0, /* bufsize */
&temp_length, DE_NULL); /* name */
error_code = gl.getError();
if (error_code == GL_INVALID_VALUE)
{
gl.getActiveSubroutineName(m_po_id, GL_VERTEX_SHADER, m_po_active_subroutines + 1, 0, /* bufsize */
&temp_length, DE_NULL); /* name */
error_code = gl.getError();
}
if (error_code != GL_INVALID_VALUE)
{
m_testCtx.getLog() << tcu::TestLog::Message << "glGetActiveSubroutineName() does not generate GL_INVALID_VALUE "
"when passed <index> argument that is greater than or equal to "
"the value of GL_ACTIVE_SUBROUTINES."
<< tcu::TestLog::EndMessage;
m_has_test_passed = false;
}
/* The error INVALID_VALUE is generated by UniformSubroutinesuiv if <count>
* is not equal to the value of ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS for the
* shader stage <shadertype>.
*/
glw::GLuint index = 0;
gl.useProgram(m_po_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() call failed.");
gl.uniformSubroutinesuiv(GL_VERTEX_SHADER, m_po_active_subroutine_uniform_locations - 1, &index);
error_code = gl.getError();
if (error_code == GL_INVALID_VALUE)
{
gl.uniformSubroutinesuiv(GL_VERTEX_SHADER, m_po_active_subroutine_uniform_locations + 1, &index);
error_code = gl.getError();
}
if (error_code != GL_INVALID_VALUE)
{
m_testCtx.getLog() << tcu::TestLog::Message << "glUniformSubroutinesiv() does not generate GL_INVALID_VALUE "
"when passed <count> argument that is not equal to the value of "
"GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS."
<< tcu::TestLog::EndMessage;
m_has_test_passed = false;
}
/* The error INVALID_VALUE is generated by UniformSubroutinesuiv if any value
* in <indices> is greater than or equal to the value of ACTIVE_SUBROUTINES
* for the shader stage.
*/
glw::GLuint invalid_subroutine_indices[4] = { (GLuint)m_po_active_subroutines, (GLuint)m_po_active_subroutines,
(GLuint)m_po_active_subroutines + 1,
(GLuint)m_po_active_subroutines + 1 };
gl.uniformSubroutinesuiv(GL_VERTEX_SHADER, m_po_active_subroutine_uniform_locations, /* count */
invalid_subroutine_indices + 0);
error_code = gl.getError();
if (error_code == GL_INVALID_VALUE)
{
gl.uniformSubroutinesuiv(GL_VERTEX_SHADER, m_po_active_subroutine_uniform_locations,
invalid_subroutine_indices + 2);
error_code = gl.getError();
}
if (error_code != GL_INVALID_VALUE)
{
m_testCtx.getLog() << tcu::TestLog::Message << "glUniformSubroutinesuiv() does not generate GL_INVALID_VALUE "
"when the value passed via <indices> argument is greater than "
"or equal to the value of GL_ACTIVE_SUBROUTINES."
<< tcu::TestLog::EndMessage;
m_has_test_passed = false;
}
/* The error INVALID_OPERATION is generated by UniformSubroutinesuiv() if any
* subroutine index in <indices> identifies a subroutine not associated with
* the type of the subroutine uniform variable assigned to the corresponding
* location.
*/
glw::GLuint invalid_subroutine_indices2[2] = { m_po_subroutine_test1_index, m_po_subroutine_test1_index };
gl.uniformSubroutinesuiv(GL_VERTEX_SHADER, m_po_active_subroutine_uniform_locations, invalid_subroutine_indices2);
error_code = gl.getError();
if (error_code != GL_INVALID_OPERATION)
{
m_testCtx.getLog() << tcu::TestLog::Message
<< "glUniformSubroutinesuiv() does not generate GL_INVALID_OPERATION "
"when the subroutine index passed via <indices> argument identifies"
"a subroutine not associated with the type of the subroutine uniform "
"assigned to the corresponding location."
<< tcu::TestLog::EndMessage;
m_has_test_passed = false;
}
/* The error INVALID_OPERATION is generated by UniformSubroutinesuiv if no
* program is active.
*/
glw::GLuint valid_subroutine_locations[2] = { 0 };
valid_subroutine_locations[m_po_subroutine_uniform_function_index] = m_po_subroutine_test1_index;
valid_subroutine_locations[m_po_subroutine_uniform_function2_index] = m_po_subroutine_test3_index;
gl.useProgram(0);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() call failed.");
gl.uniformSubroutinesuiv(GL_VERTEX_SHADER, m_po_active_subroutine_uniform_locations, valid_subroutine_locations);
error_code = gl.getError();
if (error_code != GL_INVALID_OPERATION)
{
m_testCtx.getLog() << tcu::TestLog::Message
<< "glUniformSubroutinesuiv() does not generate GL_INVALID_OPERATION "
"when called without an active program object."
<< tcu::TestLog::EndMessage;
m_has_test_passed = false;
}
/* The error INVALID_VALUE is generated by GetUniformSubroutineuiv if
* <location> is greater than or equal to the value of
* ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS for the shader stage.
*/
glw::GLuint temp_value = 0;
gl.useProgram(m_po_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() call failed.");
gl.getUniformSubroutineuiv(GL_VERTEX_SHADER, m_po_active_subroutine_uniform_locations, &temp_value);
error_code = gl.getError();
if (error_code == GL_INVALID_VALUE)
{
gl.getUniformSubroutineuiv(GL_VERTEX_SHADER, m_po_active_subroutine_uniform_locations + 1, &temp_value);
error_code = gl.getError();
}
if (error_code != GL_INVALID_VALUE)
{
m_testCtx.getLog() << tcu::TestLog::Message
<< "glGetUniformSubroutineuiv() does not generate GL_INVALID_VALUE "
"when called for location that is greater than or equal to the value "
"of GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS."
<< tcu::TestLog::EndMessage;
m_has_test_passed = false;
}
/* The error INVALID_OPERATION is generated by GetUniformSubroutineuiv if no
* program is active for the shader stage identified by <shadertype>.
*/
const glw::GLenum undefined_shader_stages[] = { GL_FRAGMENT_SHADER, GL_GEOMETRY_SHADER, GL_TESS_CONTROL_SHADER,
GL_TESS_EVALUATION_SHADER };
const unsigned int n_undefined_shader_stages = sizeof(undefined_shader_stages) / sizeof(undefined_shader_stages[0]);
for (unsigned int n_undefined_shader_stage = 0; n_undefined_shader_stage < n_undefined_shader_stages;
++n_undefined_shader_stage)
{
glw::GLenum shader_stage = undefined_shader_stages[n_undefined_shader_stage];
gl.getUniformSubroutineuiv(shader_stage, 0, /* location */
&temp_value);
error_code = gl.getError();
if (error_code != GL_INVALID_OPERATION)
{
m_testCtx.getLog() << tcu::TestLog::Message
<< "glGetUniformSubroutineuiv() does not generate GL_INVALID_OPERATION "
"when called for a shader stage that is not defined for active "
"program object."
<< tcu::TestLog::EndMessage;
m_has_test_passed = false;
}
} /* for (all undefined shader stages) */
/* All done */
if (m_has_test_passed)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
return STOP;
}
/** Constructor
*
* @param context Rendering context.
*
**/
NegativeTest2::NegativeTest2(deqp::Context& context)
: TestCase(context, "subroutine_uniform_scope", "Verifies subroutine uniforms declared in shader stage A"
"cannot be accessed from a different stage.")
, m_fs_id(0)
, m_gs_id(0)
, m_has_test_passed(true)
, m_po_id(0)
, m_tc_id(0)
, m_te_id(0)
, m_vs_id(0)
{
/* Left blank intentionally */
}
/** Deinitializes all GL objects that may have been created during test execution */
void NegativeTest2::deinit()
{
deinitGLObjects();
}
/** Deinitializes all GL objects that may have been created during test execution */
void NegativeTest2::deinitGLObjects()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
if (m_fs_id != 0)
{
gl.deleteShader(m_fs_id);
m_fs_id = 0;
}
if (m_gs_id != 0)
{
gl.deleteShader(m_gs_id);
m_gs_id = 0;
}
if (m_tc_id != 0)
{
gl.deleteShader(m_tc_id);
m_tc_id = 0;
}
if (m_te_id != 0)
{
gl.deleteShader(m_te_id);
m_te_id = 0;
}
if (m_vs_id != 0)
{
gl.deleteShader(m_vs_id);
m_vs_id = 0;
}
if (m_po_id != 0)
{
gl.deleteProgram(m_po_id);
m_po_id = 0;
}
}
/** Builds an offending program object and tries to link it. We're either expecting
* a compile-time or link-time error here.
*
* If the program object builds successfully, the test has failed.
*
* @param referencing_stage Shader stage which defines a subroutine uniform that
* should be called from fragment/geometry/tess control/
* tess evaluation/vertex shader stages.
*
**/
void NegativeTest2::executeTestCase(const Utils::_shader_stage& referencing_stage)
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
const std::string fs_body = getFragmentShaderBody(referencing_stage);
const std::string gs_body = getGeometryShaderBody(referencing_stage);
const std::string tc_body = getTessellationControlShaderBody(referencing_stage);
const std::string te_body = getTessellationEvaluationShaderBody(referencing_stage);
const std::string vs_body = getVertexShaderBody(referencing_stage);
if (Utils::buildProgram(gl, vs_body, tc_body, te_body, gs_body, fs_body, NULL, /* xfb_varyings */
0, /* n_xfb_varyings */
&m_vs_id, &m_tc_id, &m_te_id, &m_gs_id, &m_fs_id, &m_po_id))
{
/* Test program should not have built correctly ! */
m_testCtx.getLog() << tcu::TestLog::Message << "In the following program, one of the stages references "
"a subroutine that is defined in another stage. This "
"is forbidden by the specification.\n"
"\n"
"Vertex shader:\n\n"
<< vs_body.c_str() << "\n\nTessellation control shader:\n\n"
<< tc_body.c_str() << "\n\nTessellation evaluation shader:\n\n"
<< te_body.c_str() << "\n\nGeometry shader:\n\n"
<< gs_body.c_str() << "\n\nFragment shader:\n\n"
<< fs_body.c_str() << tcu::TestLog::EndMessage;
m_has_test_passed = false;
} /* if (test program was built successfully) */
/* Release the shaders & the program object that buildProgram() created */
deinitGLObjects();
}
/** Retrieves an offending fragment shader body.
*
* @param referencing_stage Shader stage which defines the subroutine uniform that
* will be called from fragment shader.
*
* @return Requested string.
**/
std::string NegativeTest2::getFragmentShaderBody(const Utils::_shader_stage& referencing_stage) const
{
std::stringstream result;
/* Form the pre-amble */
result << "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"subroutine void testSubroutineType(out vec4 test_argument);\n"
"\n"
/* Define a subroutine */
"subroutine(testSubroutineType) void fs_subroutine(out vec4 test_argument)\n"
"{\n"
" test_argument = vec4(1, 0, 0, 0);\n"
"}\n"
"\n"
/* Define output variables */
"out vec4 result;\n"
"\n"
/* Define uniforms */
"subroutine uniform testSubroutineType test_fs_subroutine;\n"
"\n"
/* Define main() */
"void main()\n"
"{\n"
" "
<< getSubroutineUniformName(referencing_stage) << "(result);\n"
"}\n";
return result.str();
}
/** Retrieves an offending geometry shader body.
*
* @param referencing_stage Shader stage which defines the subroutine uniform that
* will be called from geometry shader.
*
* @return Requested string.
**/
std::string NegativeTest2::getGeometryShaderBody(const Utils::_shader_stage& referencing_stage) const
{
std::stringstream result;
/* Form the pre-amble */
result << "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"subroutine void testSubroutineType(out vec4 test_argument);\n"
"\n"
"layout(points) in;\n"
"layout(points, max_vertices = 1) out;\n"
"\n"
/* Define a subroutine */
"subroutine(testSubroutineType) void gs_subroutine(out vec4 test_argument)\n"
"{\n"
" test_argument = vec4(0, 1, 1, 1);\n"
"}\n"
"\n"
/* Define output variables */
"out vec4 result;\n"
"\n"
/* Define uniforms */
"subroutine uniform testSubroutineType test_gs_subroutine;\n"
"\n"
/* Define main() */
"void main()\n"
"{\n"
" "
<< getSubroutineUniformName(referencing_stage) << "(result);\n"
"}\n";
return result.str();
}
/** Retrieves name of the subroutine uniform that is defined in user-specified
* shader stage.
*
* @param stage Shader stage to retrieve the subroutine uniform name for.
*
* @return As per description.
**/
std::string NegativeTest2::getSubroutineUniformName(const Utils::_shader_stage& stage) const
{
std::string result = "?";
switch (stage)
{
case Utils::SHADER_STAGE_FRAGMENT:
{
result = "test_fs_subroutine";
break;
}
case Utils::SHADER_STAGE_GEOMETRY:
{
result = "test_gs_subroutine";
break;
}
case Utils::SHADER_STAGE_TESSELLATION_CONTROL:
{
result = "test_tc_subroutine";
break;
}
case Utils::SHADER_STAGE_TESSELLATION_EVALUATION:
{
result = "test_te_subroutine";
break;
}
case Utils::SHADER_STAGE_VERTEX:
{
result = "test_vs_subroutine";
break;
}
default:
{
TCU_FAIL("Unrecognized shader stage requested");
}
} /* switch (stage) */
return result;
}
/** Retrieves an offending tessellation control shader body.
*
* @param referencing_stage Shader stage which defines the subroutine uniform that
* will be called from tessellation control shader.
*
* @return Requested string.
**/
std::string NegativeTest2::getTessellationControlShaderBody(const Utils::_shader_stage& referencing_stage) const
{
std::stringstream result;
/* Form the pre-amble */
result << "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"layout(vertices = 4) out;\n"
"\n"
"subroutine void testSubroutineType(out vec4 test_argument);\n"
"\n"
/* Define a subroutine */
"subroutine(testSubroutineType) void tc_subroutine(out vec4 test_argument)\n"
"{\n"
" test_argument = vec4(0, 0, 1, 0);\n"
"}\n"
"\n"
/* Define uniforms */
"subroutine uniform testSubroutineType test_tc_subroutine;\n"
"\n"
/* Define main() */
"void main()\n"
"{\n"
" "
<< getSubroutineUniformName(referencing_stage) << "(gl_out[gl_InvocationID].gl_Position);\n"
"}\n";
return result.str();
}
/** Retrieves an offending tessellation evaluation shader body.
*
* @param referencing_stage Shader stage which defines the subroutine uniform that
* will be called from tessellation evaluation shader.
*
* @return Requested string.
**/
std::string NegativeTest2::getTessellationEvaluationShaderBody(const Utils::_shader_stage& referencing_stage) const
{
std::stringstream result;
/* Form the pre-amble */
result << "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"layout(quads) in;\n"
"\n"
"subroutine void testSubroutineType(out vec4 test_argument);\n"
"\n"
/* Define a subroutine */
"subroutine(testSubroutineType) void te_subroutine(out vec4 test_argument)\n"
"{\n"
" test_argument = vec4(1, 1, 1, 1);\n"
"}\n"
"\n"
/* Define uniforms */
"subroutine uniform testSubroutineType test_te_subroutine;\n"
"\n"
/* Define main() */
"void main()\n"
"{\n"
" "
<< getSubroutineUniformName(referencing_stage) << "(gl_Position);\n"
"}\n";
return result.str();
}
/** Retrieves an offending vertex shader body.
*
* @param referencing_stage Shader stage which defines the subroutine uniform that
* will be called from vertex shader.
*
* @return Requested string.
**/
std::string NegativeTest2::getVertexShaderBody(const Utils::_shader_stage& referencing_stage) const
{
std::stringstream result;
/* Form the pre-amble */
result << "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"subroutine void testSubroutineType(out vec4 test_argument);\n"
"\n"
/* Define a subroutine */
"subroutine(testSubroutineType) void vs_subroutine(out vec4 test_argument)\n"
"{\n"
" test_argument = vec4(0, 1, 0, 0);\n"
"}\n"
"\n"
/* Define uniforms */
"subroutine uniform testSubroutineType test_vs_subroutine;\n"
"\n"
/* Define main() */
"void main()\n"
"{\n"
" "
<< getSubroutineUniformName(referencing_stage) << "(gl_Position);\n"
"}\n";
return result.str();
}
/** Executes test iteration.
*
* @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed.
*/
tcu::TestNode::IterateResult NegativeTest2::iterate()
{
/* Do not execute the test if GL_ARB_shader_subroutine is not supported */
if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_shader_subroutine"))
{
throw tcu::NotSupportedError("GL_ARB_shader_subroutine is not supported.");
}
/* Iterate over all shader stages and execute the checks */
for (int referencing_stage = static_cast<int>(Utils::SHADER_STAGE_FIRST);
referencing_stage < static_cast<int>(Utils::SHADER_STAGE_COUNT); ++referencing_stage)
{
executeTestCase(static_cast<Utils::_shader_stage>(referencing_stage));
} /* for (all test cases) */
/* All done */
if (m_has_test_passed)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
return STOP;
}
/** Constructor.
*
* @param context Rendering context.
*
**/
NegativeTest3::NegativeTest3(deqp::Context& context)
: TestCase(context, "missing_subroutine_keyword", "Verifies that subroutine keyword is necessary when declaring a "
"subroutine uniforn and a compilation error occurs without it.")
, m_has_test_passed(true)
, m_so_id(0)
{
/* Left blank intentionally */
}
/** Deinitializes all GL objects that may have been created during test execution */
void NegativeTest3::deinit()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
if (m_so_id != 0)
{
gl.deleteShader(m_so_id);
m_so_id = 0;
}
}
/** Verifies that broken shader (for user-specified shader stage) does not compile.
*
* @param shader_stage Shader stage to use for the test.
**/
void NegativeTest3::executeTest(const Utils::_shader_stage& shader_stage)
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Generate a new shader object */
m_so_id = gl.createShader(Utils::getGLenumForShaderStage(shader_stage));
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() call failed.");
/* Assign body to the shader */
std::string body;
const char* body_raw_ptr = DE_NULL;
switch (shader_stage)
{
case Utils::SHADER_STAGE_VERTEX:
body = getVertexShaderBody();
break;
case Utils::SHADER_STAGE_TESSELLATION_CONTROL:
body = getTessellationControlShaderBody();
break;
case Utils::SHADER_STAGE_TESSELLATION_EVALUATION:
body = getTessellationEvaluationShaderBody();
break;
case Utils::SHADER_STAGE_GEOMETRY:
body = getGeometryShaderBody();
break;
case Utils::SHADER_STAGE_FRAGMENT:
body = getFragmentShaderBody();
break;
default:
{
TCU_FAIL("Unrecognized shader stage requested");
}
} /* switch (shader_stage) */
body_raw_ptr = body.c_str();
gl.shaderSource(m_so_id, 1 /* count */, &body_raw_ptr, DE_NULL /* length */);
GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() call failed.");
/* Try to compile the shader */
glw::GLint compile_status = 0;
gl.compileShader(m_so_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader() call failed.");
gl.getShaderiv(m_so_id, GL_COMPILE_STATUS, &compile_status);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv() call failed.");
if (compile_status == GL_TRUE)
{
m_testCtx.getLog() << tcu::TestLog::Message << "The following shader was expected to fail to compile but was "
"accepted by the compiler:\n"
"\n"
<< body.c_str() << tcu::TestLog::EndMessage;
m_has_test_passed = false;
}
/* Good to release the shader at this point */
gl.deleteShader(m_so_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteShader() call failed.");
}
/** Retrieves body of a broken fragment shader.
*
* @return Requested string.
**/
std::string NegativeTest3::getFragmentShaderBody() const
{
return "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"subroutine void testSubroutineType(inout vec4 test);\n"
"\n"
"void testSubroutine1(inout vec4 test)\n"
"{\n"
" test += vec4(3, 4, 5, 6);\n"
"}\n"
"\n"
"uniform testSubroutineType subroutineFunction;\n"
"out vec4 result;\n"
"\n"
"void main()\n"
"{\n"
" vec4 test = vec4(2, 3, 4, 5);\n"
"\n"
" subroutineFunction(test);\n"
"\n"
" result = test;\n"
"}\n";
}
/** Retrieves body of a broken geometry shader.
*
* @return Requested string.
**/
std::string NegativeTest3::getGeometryShaderBody() const
{
return "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"layout(points) in;\n"
"layout(points, max_vertices = 1) out;\n"
"\n"
"subroutine void testSubroutineType(inout vec4 test);\n"
"\n"
"void testSubroutine1(inout vec4 test)\n"
"{\n"
" test += vec4(3, 4, 5, 6);\n"
"}\n"
"\n"
"uniform testSubroutineType subroutineFunction;\n"
"\n"
"void main()\n"
"{\n"
" vec4 test = vec4(2, 3, 4, 5);\n"
"\n"
" subroutineFunction(test);\n"
"\n"
" gl_Position = test;\n"
" EmitVertex();\n"
"}\n";
}
/** Retrieves body of a broken tessellation control shader.
*
* @return Requested string.
**/
std::string NegativeTest3::getTessellationControlShaderBody() const
{
return "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"layout(vertices=4) out;\n"
"\n"
"subroutine void testSubroutineType(inout vec4 test);\n"
"\n"
"void testSubroutine1(inout vec4 test)\n"
"{\n"
" test += vec4(1, 2, 3, 4);\n"
"}\n"
"\n"
"uniform testSubroutineType subroutineFunction;\n"
"\n"
"void main()\n"
"{\n"
" vec4 test = vec4(0, 1, 2, 3);\n"
"\n"
" subroutineFunction(test);\n"
"\n"
" gl_out[gl_InvocationID].gl_Position = test;\n"
"}\n";
}
/** Retrieves body of a broken tessellation evaluation shader.
*
* @return Requested string.
**/
std::string NegativeTest3::getTessellationEvaluationShaderBody() const
{
return "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"layout(quads) in;\n"
"\n"
"subroutine void testSubroutineType(inout vec4 test);\n"
"\n"
"void testSubroutine1(inout vec4 test)\n"
"{\n"
" test += vec4(2, 3, 4, 5);\n"
"}\n"
"\n"
"uniform testSubroutineType subroutineFunction;\n"
"\n"
"void main()\n"
"{\n"
" vec4 test = vec4(1, 2, 3, 4);\n"
"\n"
" subroutineFunction(test);\n"
"\n"
" gl_Position = test;\n"
"}\n";
}
/** Retrieves body of a broken vertex shader.
*
* @return Requested string.
**/
std::string NegativeTest3::getVertexShaderBody() const
{
return "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"subroutine void testSubroutineType(inout vec4 test);\n"
"\n"
"void testSubroutine1(inout vec4 test)\n"
"{\n"
" test += vec4(0, 1, 2, 3);\n"
"}\n"
"\n"
"uniform testSubroutineType subroutineFunction;\n"
"\n"
"void main()\n"
"{\n"
" subroutineFunction(gl_Position);\n"
"}\n";
}
/** Executes test iteration.
*
* @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed.
*/
tcu::TestNode::IterateResult NegativeTest3::iterate()
{
/* Do not execute the test if GL_ARB_shader_subroutine is not supported */
if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_shader_subroutine"))
{
throw tcu::NotSupportedError("GL_ARB_shader_subroutine is not supported.");
}
/* Iterate over all shader stages */
for (int shader_stage = static_cast<int>(Utils::SHADER_STAGE_FIRST);
shader_stage < static_cast<int>(Utils::SHADER_STAGE_COUNT); ++shader_stage)
{
executeTest(static_cast<Utils::_shader_stage>(shader_stage));
} /* for (all shader stages) */
/* Done */
if (m_has_test_passed)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
return STOP;
}
/** Constructor.
*
* @param context Rendering context.
*
**/
NegativeTest4::NegativeTest4(deqp::Context& context)
: TestCase(context, "subroutines_incompatible_with_subroutine_type",
"Verifies that a compile-time error is generated when arguments and "
"return type do not match beween the function and each associated "
"subroutine type.")
, m_has_test_passed(true)
, m_so_id(0)
{
/* Left blank intentionally */
}
/** Deinitializes GL objects that may have been created during test
* execution.
**/
void NegativeTest4::deinit()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
if (m_so_id != 0)
{
gl.deleteShader(m_so_id);
m_so_id = 0;
}
}
/** Retrieves body of a shader of user-specified type that should be used
* for a single test iteration. The shader will define user-specified number
* of subroutine types, with the last type either defining an additional argument
* or using a different return type.
* A subroutine (claimed compatible with *all* subroutine types) will also be
* defined in the shader.
*
* @param shader_stage Shader stage to use for the query.
* @param n_subroutine_types Overall number of subroutine types that will be
* declared & used in the shader. Please see description
* for more details.
*
* @return Requested string.
**/
std::string NegativeTest4::getShaderBody(const Utils::_shader_stage& shader_stage,
const unsigned int& n_subroutine_types, const _test_case& test_case) const
{
std::stringstream result_sstream;
/* Form the pre-amble */
result_sstream << "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n";
/* Inject stage-specific code */
switch (shader_stage)
{
case Utils::SHADER_STAGE_GEOMETRY:
{
result_sstream << "layout (points) in;\n"
"layout (points, max_vertices = 1) out;\n"
"\n";
break;
}
case Utils::SHADER_STAGE_TESSELLATION_CONTROL:
{
result_sstream << "layout (vertices = 4) out;\n"
"\n";
break;
}
case Utils::SHADER_STAGE_TESSELLATION_EVALUATION:
{
result_sstream << "layout (quads) in;\n"
"\n";
break;
}
default:
break;
} /* switch (shader_stage) */
/* Insert subroutine type declarations */
for (unsigned int n_subroutine_type = 0; n_subroutine_type < n_subroutine_types - 1; ++n_subroutine_type)
{
result_sstream << "subroutine void subroutineType" << n_subroutine_type << "(inout vec3 argument);\n";
} /* for (all subroutine types) */
switch (test_case)
{
case TEST_CASE_INCOMPATIBLE_ARGUMENT_LIST:
{
result_sstream << "subroutine void subroutineType" << (n_subroutine_types - 1)
<< "(inout vec3 argument, out vec4 argument2);\n";
break;
}
case TEST_CASE_INCOMPATIBLE_RETURN_TYPE:
{
result_sstream << "subroutine int subroutineType" << (n_subroutine_types - 1) << "(inout vec3 argument);\n";
break;
}
default:
{
TCU_FAIL("Unrecognized test case");
}
} /* switch (test_case) */
/* Insert subroutine declarations */
result_sstream << "subroutine(";
for (unsigned int n_subroutine_type = 0; n_subroutine_type < n_subroutine_types; ++n_subroutine_type)
{
result_sstream << "subroutineType" << n_subroutine_type;
if (n_subroutine_type != (n_subroutine_types - 1))
{
result_sstream << ", ";
}
} /* for (all subroutine types) */
result_sstream << ") void function(inout vec3 argument)\n"
"{\n"
" argument = vec3(1, 2, 3);\n"
"}\n"
"\n";
/* Insert remaining required stage-specific bits */
switch (shader_stage)
{
case Utils::SHADER_STAGE_FRAGMENT:
{
result_sstream << "out vec4 result;\n"
"\n"
"void main()\n"
"{\n"
" result = vec4(1, 2, 3, 4);\n"
"}\n";
break;
}
case Utils::SHADER_STAGE_GEOMETRY:
{
result_sstream << "void main()\n"
"{\n"
" gl_Position = vec4(1, 2, 3, 4);\n"
" EmitVertex();\n"
"}\n";
break;
}
case Utils::SHADER_STAGE_TESSELLATION_CONTROL:
{
result_sstream << "void main()\n"
"{\n"
" gl_TessLevelInner[0] = 1;\n"
" gl_TessLevelInner[1] = 1;\n"
" gl_TessLevelOuter[0] = 1;\n"
" gl_TessLevelOuter[1] = 1;\n"
" gl_TessLevelOuter[2] = 1;\n"
" gl_TessLevelOuter[3] = 1;\n"
" gl_out[gl_InvocationID].gl_Position = vec4(2, 3, 4, 5);\n"
"}\n";
break;
}
case Utils::SHADER_STAGE_TESSELLATION_EVALUATION:
case Utils::SHADER_STAGE_VERTEX:
{
result_sstream << "void main()\n"
"{\n"
" gl_Position = vec4(1, 2, 3, 4);\n"
"}\n";
break;
}
default:
{
TCU_FAIL("Unrecognized shader stage");
}
} /* switch (shader_stage) */
return result_sstream.str();
}
/** Executes test iteration.
*
* @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed.
*/
tcu::TestNode::IterateResult NegativeTest4::iterate()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Do not execute the test if GL_ARB_shader_subroutine is not supported */
if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_shader_subroutine"))
{
throw tcu::NotSupportedError("GL_ARB_shader_subroutine is not supported.");
}
/* Iterate over all shader stages.. */
for (int shader_stage = static_cast<int>(Utils::SHADER_STAGE_FIRST);
shader_stage != static_cast<int>(Utils::SHADER_STAGE_COUNT); ++shader_stage)
{
/* For each shader stage, we will be trying to compile a number of invalid shaders.
* Each shader defines N different subroutine types. (N-1) of them are compatible
* with a subroutine, but exactly 1 will be mismatched. The test passes if GLSL
* compiler correctly detects that all shaders we will be trying to compile are
* broken.
*/
const glw::GLenum shader_type = Utils::getGLenumForShaderStage(static_cast<Utils::_shader_stage>(shader_stage));
for (unsigned int n_subroutine_types = 1; n_subroutine_types < 6; /* arbitrary number */
++n_subroutine_types)
{
for (int test_case = static_cast<int>(TEST_CASE_FIRST); test_case != static_cast<int>(TEST_CASE_COUNT);
++test_case)
{
std::string body;
const char* body_raw_ptr = NULL;
glw::GLint compile_status = GL_FALSE;
body = getShaderBody(static_cast<Utils::_shader_stage>(shader_stage), n_subroutine_types,
static_cast<_test_case>(test_case));
body_raw_ptr = body.c_str();
/* Try to compile the shader */
m_so_id = gl.createShader(shader_type);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() call failed");
gl.shaderSource(m_so_id, 1 /* count */, &body_raw_ptr, DE_NULL);
GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() call failed");
gl.compileShader(m_so_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader() call failed");
gl.getShaderiv(m_so_id, GL_COMPILE_STATUS, &compile_status);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv() call failed.");
if (compile_status == GL_TRUE)
{
m_testCtx.getLog() << tcu::TestLog::Message << "A malformed "
<< Utils::getShaderStageString(static_cast<Utils::_shader_stage>(shader_stage))
<< " compiled successfully "
"("
<< n_subroutine_types << " subroutine types "
"were defined)."
<< tcu::TestLog::EndMessage;
m_has_test_passed = false;
}
/* Release the object */
gl.deleteShader(m_so_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteShader() call failed.");
} /* for (all test cases) */
} /* for (a number of different subroutine type declarations) */
} /* for (all shader stages) */
/* Done */
if (m_has_test_passed)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
return STOP;
}
/** Constructor.
*
* @param context Rendering context.
*
**/
NegativeTest5::NegativeTest5(deqp::Context& context)
: TestCase(context, "subroutine_uniform_wo_matching_subroutines",
"Verifies that a link- or compile-time error occurs when "
"trying to link a program with no subroutine for subroutine "
"uniform variable.")
, m_fs_id(0)
, m_gs_id(0)
, m_has_test_passed(true)
, m_po_id(0)
, m_tc_id(0)
, m_te_id(0)
, m_vs_id(0)
{
/* Left blank intentionally */
}
/** Deinitializes all GL objects that may have been created during test execution */
void NegativeTest5::deinit()
{
deinitIteration();
}
/** Deinitializes all GL objects that may have been created during a single test
* iteration.
***/
void NegativeTest5::deinitIteration()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
if (m_fs_id != 0)
{
gl.deleteShader(m_fs_id);
m_fs_id = 0;
}
if (m_gs_id != 0)
{
gl.deleteShader(m_gs_id);
m_gs_id = 0;
}
if (m_po_id != 0)
{
gl.deleteProgram(m_po_id);
m_po_id = 0;
}
if (m_tc_id != 0)
{
gl.deleteShader(m_tc_id);
m_tc_id = 0;
}
if (m_te_id != 0)
{
gl.deleteShader(m_te_id);
m_te_id = 0;
}
if (m_vs_id != 0)
{
gl.deleteShader(m_vs_id);
m_vs_id = 0;
}
}
/** Executes a single test iteration.
*
* If the iteration fails, m_has_test_passed will be set to false.
*
* @param shader_stage Shader stage, for which a subroutine uniform should be
* declared in the shader without a matching subroutine.
**/
void NegativeTest5::executeIteration(const Utils::_shader_stage& shader_stage)
{
std::string fs_body = getFragmentShaderBody(shader_stage == Utils::SHADER_STAGE_FRAGMENT);
std::string gs_body = getGeometryShaderBody(shader_stage == Utils::SHADER_STAGE_GEOMETRY);
std::string tc_body = getTessellationControlShaderBody(shader_stage == Utils::SHADER_STAGE_TESSELLATION_CONTROL);
std::string te_body =
getTessellationEvaluationShaderBody(shader_stage == Utils::SHADER_STAGE_TESSELLATION_EVALUATION);
std::string vs_body = getVertexShaderBody(shader_stage == Utils::SHADER_STAGE_VERTEX);
if (Utils::buildProgram(m_context.getRenderContext().getFunctions(), vs_body, tc_body, te_body, gs_body, fs_body,
DE_NULL, /* xfb_varyings */
DE_NULL, /* n_xfb_varyings */
&m_vs_id, &m_tc_id, &m_te_id, &m_gs_id, &m_fs_id, &m_po_id))
{
/* None of the test programs should ever build successfully */
m_testCtx.getLog() << tcu::TestLog::Message
<< "A program object, consisting of the following shaders, has linked"
" correctly. One of the shaders defines a subroutine uniform but does "
"not implement any function that matches subroutine type of the uniform."
" This should have resulted in a compilation/link-time error.\n"
"\n"
"Vertex shader:\n"
"\n"
<< vs_body << "\n"
"Tessellation control shader:\n"
"\n"
<< tc_body << "\n"
"Tessellation evaluation shader:\n"
"\n"
<< te_body << "\n"
"Geometry shader:\n"
"\n"
<< gs_body << "\n"
"Fragment shader:\n"
"\n"
<< fs_body << tcu::TestLog::EndMessage;
m_has_test_passed = false;
}
}
/** Retrieves fragment shader body.
*
* @param include_invalid_subroutine_uniform_declaration true if the shader should declare
* a subroutine uniform without
* a matching subroutine, false otherwise.
*
* @return Requested string.
**/
std::string NegativeTest5::getFragmentShaderBody(bool include_invalid_subroutine_uniform_declaration) const
{
std::stringstream result_sstream;
result_sstream << "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n";
if (include_invalid_subroutine_uniform_declaration)
{
result_sstream << "subroutine void subroutineTestTypeFS(out vec4 test);\n"
"\n"
"subroutine uniform subroutineTestTypeFS test_subroutineFS;\n";
}
result_sstream << "\n"
"out vec4 result;\n"
"\n"
"void main()\n"
"{\n";
if (include_invalid_subroutine_uniform_declaration)
{
result_sstream << " test_subroutineFS(result);\n";
}
else
{
result_sstream << " result = vec4(0, 1, 2, 3);\n";
}
result_sstream << "}\n";
return result_sstream.str();
}
/** Retrieves geometry shader body.
*
* @param include_invalid_subroutine_uniform_declaration true if the shader should declare
* a subroutine uniform without
* a matching subroutine, false otherwise.
*
* @return Requested string.
**/
std::string NegativeTest5::getGeometryShaderBody(bool include_invalid_subroutine_uniform_declaration) const
{
std::stringstream result_sstream;
result_sstream << "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"layout (points) in;\n"
"layout (points, max_vertices = 1) out;\n"
"\n";
if (include_invalid_subroutine_uniform_declaration)
{
result_sstream << "subroutine void subroutineTestTypeGS(out vec4 test);\n"
"\n"
"subroutine uniform subroutineTestTypeGS test_subroutineGS;\n";
}
result_sstream << "\n"
"void main()\n"
"{\n";
if (include_invalid_subroutine_uniform_declaration)
{
result_sstream << " test_subroutineGS(gl_Position);\n";
}
else
{
result_sstream << " gl_Position = vec4(0, 1, 2, 3);\n";
}
result_sstream << "EmitVertex();\n"
"}\n";
return result_sstream.str();
}
/** Retrieves tessellation control shader body.
*
* @param include_invalid_subroutine_uniform_declaration true if the shader should declare
* a subroutine uniform without
* a matching subroutine, false otherwise.
*
* @return Requested string.
**/
std::string NegativeTest5::getTessellationControlShaderBody(bool include_invalid_subroutine_uniform_declaration) const
{
std::stringstream result_sstream;
result_sstream << "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"layout (vertices = 4) out;\n"
"\n";
if (include_invalid_subroutine_uniform_declaration)
{
result_sstream << "subroutine void subroutineTestTypeTC(out vec4 test);\n"
"\n"
"subroutine uniform subroutineTestTypeTC test_subroutineTC;\n";
}
result_sstream << "\n"
"void main()\n"
"{\n";
if (include_invalid_subroutine_uniform_declaration)
{
result_sstream << " test_subroutineTC(gl_out[gl_InvocationID].gl_Position);\n";
}
else
{
result_sstream << " gl_out[gl_InvocationID].gl_Position = vec4(0, 1, 2, 3);\n";
}
result_sstream << "}\n";
return result_sstream.str();
}
/** Retrieves tessellation evaluation body.
*
* @param include_invalid_subroutine_uniform_declaration true if the shader should declare
* a subroutine uniform without
* a matching subroutine, false otherwise.
*
* @return Requested string.
**/
std::string NegativeTest5::getTessellationEvaluationShaderBody(
bool include_invalid_subroutine_uniform_declaration) const
{
std::stringstream result_sstream;
result_sstream << "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"layout (quads) in;\n"
"\n";
if (include_invalid_subroutine_uniform_declaration)
{
result_sstream << "subroutine void subroutineTestTypeTE(out vec4 test);\n"
"\n"
"subroutine uniform subroutineTestTypeTE test_subroutineTE;\n";
}
result_sstream << "\n"
"void main()\n"
"{\n";
if (include_invalid_subroutine_uniform_declaration)
{
result_sstream << " test_subroutineTE(gl_Position);\n";
}
else
{
result_sstream << " gl_Position = vec4(0, 1, 2, 3);\n";
}
result_sstream << "}\n";
return result_sstream.str();
}
/** Retrieves vertex shader body.
*
* @param include_invalid_subroutine_uniform_declaration true if the shader should declare
* a subroutine uniform without
* a matching subroutine, false otherwise.
*
* @return Requested string.
**/
std::string NegativeTest5::getVertexShaderBody(bool include_invalid_subroutine_uniform_declaration) const
{
std::stringstream result_sstream;
result_sstream << "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n";
if (include_invalid_subroutine_uniform_declaration)
{
result_sstream << "subroutine void subroutineTestTypeVS(out vec4 test);\n"
"\n"
"subroutine uniform subroutineTestTypeVS test_subroutineVS;\n";
}
result_sstream << "\n"
"void main()\n"
"{\n";
if (include_invalid_subroutine_uniform_declaration)
{
result_sstream << " test_subroutineVS(gl_Position);\n";
}
else
{
result_sstream << " gl_Position = vec4(0, 1, 2, 3);\n";
}
result_sstream << "}\n";
return result_sstream.str();
}
/** Executes test iteration.
*
* @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed.
*/
tcu::TestNode::IterateResult NegativeTest5::iterate()
{
/* Do not execute the test if GL_ARB_shader_subroutine is not supported */
if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_shader_subroutine"))
{
throw tcu::NotSupportedError("GL_ARB_shader_subroutine is not supported.");
}
/* Iterate over all shader stages. Iteration-specific shader stage defines a subroutine type &
* a corresponding subroutine uniform, for which no compatible subroutines are available. All
* other shader stages are defined correctly.
*/
for (int shader_stage = static_cast<int>(Utils::SHADER_STAGE_FIRST);
shader_stage < static_cast<int>(Utils::SHADER_STAGE_COUNT); ++shader_stage)
{
executeIteration(static_cast<Utils::_shader_stage>(shader_stage));
deinitIteration();
} /* for (all shader stages) */
/* All done */
if (m_has_test_passed)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
return STOP;
}
/** Constructor.
*
* @param context Rendering context.
*
**/
NegativeTest6::NegativeTest6(deqp::Context& context)
: TestCase(context, "two_duplicate_functions_one_being_a_subroutine",
"Verifies that a link- or compile-time error occurs if any shader in "
"a program object includes two functions with the same name and one "
"of which is associated with a subroutine type.")
, m_fs_id(0)
, m_gs_id(0)
, m_has_test_passed(true)
, m_po_id(0)
, m_tc_id(0)
, m_te_id(0)
, m_vs_id(0)
{
/* Left blank intentionally */
}
/** Deinitializes all GL objects that may have been created during test execution */
void NegativeTest6::deinit()
{
deinitIteration();
}
/** Deinitializes all GL objects that may have been created during a single test
* iteration.
***/
void NegativeTest6::deinitIteration()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
if (m_fs_id != 0)
{
gl.deleteShader(m_fs_id);
m_fs_id = 0;
}
if (m_gs_id != 0)
{
gl.deleteShader(m_gs_id);
m_gs_id = 0;
}
if (m_po_id != 0)
{
gl.deleteProgram(m_po_id);
m_po_id = 0;
}
if (m_tc_id != 0)
{
gl.deleteShader(m_tc_id);
m_tc_id = 0;
}
if (m_te_id != 0)
{
gl.deleteShader(m_te_id);
m_te_id = 0;
}
if (m_vs_id != 0)
{
gl.deleteShader(m_vs_id);
m_vs_id = 0;
}
}
/** Executes a single test iteration.
*
* If the iteration fails, m_has_test_passed will be set to false.
*
* @param shader_stage Shader stage, for which two duplicate functions
* (one additionally marked as subroutine) should
* be defined.
**/
void NegativeTest6::executeIteration(const Utils::_shader_stage& shader_stage)
{
std::string fs_body = getFragmentShaderBody(shader_stage == Utils::SHADER_STAGE_FRAGMENT);
std::string gs_body = getGeometryShaderBody(shader_stage == Utils::SHADER_STAGE_GEOMETRY);
std::string tc_body = getTessellationControlShaderBody(shader_stage == Utils::SHADER_STAGE_TESSELLATION_CONTROL);
std::string te_body =
getTessellationEvaluationShaderBody(shader_stage == Utils::SHADER_STAGE_TESSELLATION_EVALUATION);
std::string vs_body = getVertexShaderBody(shader_stage == Utils::SHADER_STAGE_VERTEX);
if (Utils::buildProgram(m_context.getRenderContext().getFunctions(), vs_body, tc_body, te_body, gs_body, fs_body,
DE_NULL, /* xfb_varyings */
DE_NULL, /* n_xfb_varyings */
&m_vs_id, &m_tc_id, &m_te_id, &m_gs_id, &m_fs_id, &m_po_id))
{
/* None of the test programs should ever build successfully */
m_testCtx.getLog() << tcu::TestLog::Message
<< "A program object, consisting of the following shaders, has linked"
" correctly. This is invalid, because one of the shaders defines two"
" functions with the same name, with an exception that one of the"
" functions is marked as a subroutine.\n"
"\n"
"Vertex shader:\n"
"\n"
<< vs_body << "\n"
"Tessellation control shader:\n"
"\n"
<< tc_body << "\n"
"Tessellation evaluation shader:\n"
"\n"
<< te_body << "\n"
"Geometry shader:\n"
"\n"
<< gs_body << "\n"
"Fragment shader:\n"
"\n"
<< fs_body << tcu::TestLog::EndMessage;
m_has_test_passed = false;
}
}
/** Retrieves fragment shader body.
*
* @param include_invalid_declaration true if the shader should include duplicate function
* declaration.
*
* @return Requested string.
**/
std::string NegativeTest6::getFragmentShaderBody(bool include_invalid_declaration) const
{
std::stringstream result_sstream;
result_sstream << "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n";
if (include_invalid_declaration)
{
result_sstream << "subroutine void subroutineTestTypeFS(out vec4 test);\n"
"\n"
"subroutine(subroutineTestTypeFS) void test_impl1(out vec4 test)\n"
"{\n"
" test = vec4(1, 2, 3, 4);\n"
"}\n"
"\n"
"void test_impl1(out vec4 test)\n"
"{\n"
" test = vec4(2, 3, 4, 5);\n"
"}\n"
"\n"
"subroutine uniform subroutineTestTypeFS test_subroutineFS;\n";
}
result_sstream << "\n"
"out vec4 result;\n"
"\n"
"void main()\n"
"{\n";
if (include_invalid_declaration)
{
result_sstream << " test_subroutineFS(result);\n";
}
else
{
result_sstream << " result = vec4(0, 1, 2, 3);\n";
}
result_sstream << "}\n";
return result_sstream.str();
}
/** Retrieves geometry shader body.
*
* @param include_invalid_declaration true if the shader should include duplicate function
* declaration.
*
* @return Requested string.
**/
std::string NegativeTest6::getGeometryShaderBody(bool include_invalid_declaration) const
{
std::stringstream result_sstream;
result_sstream << "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"layout (points) in;\n"
"layout (points, max_vertices = 1) out;\n"
"\n";
if (include_invalid_declaration)
{
result_sstream << "subroutine void subroutineTestTypeGS(out vec4 test);\n"
"\n"
"subroutine(subroutineTestTypeGS) void test_impl1(out vec4 test)\n"
"{\n"
" test = vec4(1, 2, 3, 4);\n"
"}\n"
"\n"
"void test_impl1(out vec4 test)\n"
"{\n"
" test = vec4(2, 3, 4, 5);\n"
"}\n"
"\n"
"subroutine uniform subroutineTestTypeGS test_subroutineGS;\n";
}
result_sstream << "\n"
"void main()\n"
"{\n";
if (include_invalid_declaration)
{
result_sstream << " test_subroutineGS(gl_Position);\n";
}
else
{
result_sstream << " gl_Position = vec4(0, 1, 2, 3);\n";
}
result_sstream << "EmitVertex();\n"
"}\n";
return result_sstream.str();
}
/** Retrieves tessellation control shader body.
*
* @param include_invalid_declaration true if the shader should include duplicate function
* declaration.
*
* @return Requested string.
**/
std::string NegativeTest6::getTessellationControlShaderBody(bool include_invalid_declaration) const
{
std::stringstream result_sstream;
result_sstream << "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"layout (vertices = 4) out;\n"
"\n";
if (include_invalid_declaration)
{
result_sstream << "subroutine void subroutineTestTypeTC(out vec4 test);\n"
"\n"
"subroutine(subroutineTestTypeTC) void test_impl1(out vec4 test)\n"
"{\n"
" test = vec4(1, 2, 3, 4);\n"
"}\n"
"\n"
"void test_impl1(out vec4 test)\n"
"{\n"
" test = vec4(2, 3, 4, 5);\n"
"}\n"
"\n"
"subroutine uniform subroutineTestTypeTC test_subroutineTC;\n";
}
result_sstream << "\n"
"void main()\n"
"{\n";
if (include_invalid_declaration)
{
result_sstream << " test_subroutineTC(gl_out[gl_InvocationID].gl_Position);\n";
}
else
{
result_sstream << " gl_out[gl_InvocationID].gl_Position = vec4(0, 1, 2, 3);\n";
}
result_sstream << "}\n";
return result_sstream.str();
}
/** Retrieves tessellation evaluation body.
*
* @param include_invalid_declaration true if the shader should include duplicate function
* declaration.
*
* @return Requested string.
**/
std::string NegativeTest6::getTessellationEvaluationShaderBody(bool include_invalid_declaration) const
{
std::stringstream result_sstream;
result_sstream << "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"layout (quads) in;\n"
"\n";
if (include_invalid_declaration)
{
result_sstream << "subroutine void subroutineTestTypeTE(out vec4 test);\n"
"\n"
"subroutine(subroutineTestTypeTE) void test_impl1(out vec4 test)\n"
"{\n"
" test = vec4(1, 2, 3, 4);\n"
"}\n"
"\n"
"void test_impl1(out vec4 test)\n"
"{\n"
" test = vec4(2, 3, 4, 5);\n"
"}\n"
"\n"
"subroutine uniform subroutineTestTypeTE test_subroutineTE;\n";
}
result_sstream << "\n"
"void main()\n"
"{\n";
if (include_invalid_declaration)
{
result_sstream << " test_subroutineTE(gl_Position);\n";
}
else
{
result_sstream << " gl_Position = vec4(0, 1, 2, 3);\n";
}
result_sstream << "}\n";
return result_sstream.str();
}
/** Retrieves vertex shader body.
*
* @param include_invalid_declaration true if the shader should include duplicate function
* declaration.
*
* @return Requested string.
**/
std::string NegativeTest6::getVertexShaderBody(bool include_invalid_declaration) const
{
std::stringstream result_sstream;
result_sstream << "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n";
if (include_invalid_declaration)
{
result_sstream << "subroutine void subroutineTestTypeVS(out vec4 test);\n"
"\n"
"subroutine(subroutineTestTypeVS) void test_impl1(out vec4 test)\n"
"{\n"
" test = vec4(1, 2, 3, 4);\n"
"}\n"
"\n"
"void test_impl1(out vec4 test)\n"
"{\n"
" test = vec4(2, 3, 4, 5);\n"
"}\n"
"\n"
"subroutine uniform subroutineTestTypeVS test_subroutineVS;\n";
}
result_sstream << "\n"
"void main()\n"
"{\n";
if (include_invalid_declaration)
{
result_sstream << " test_subroutineVS(gl_Position);\n";
}
else
{
result_sstream << " gl_Position = vec4(0, 1, 2, 3);\n";
}
result_sstream << "}\n";
return result_sstream.str();
}
/** Executes test iteration.
*
* @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed.
*/
tcu::TestNode::IterateResult NegativeTest6::iterate()
{
/* Do not execute the test if GL_ARB_shader_subroutine is not supported */
if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_shader_subroutine"))
{
throw tcu::NotSupportedError("GL_ARB_shader_subroutine is not supported.");
}
/* Iterate over all shader stages. In each iteration, we will inject invalid
* duplicate function declarations to iteration-specific shader stage. All other
* shader stages will be assigned valid bodies. Test should fail if the program
* links successfully.
*/
for (int shader_stage = static_cast<int>(Utils::SHADER_STAGE_FIRST);
shader_stage < static_cast<int>(Utils::SHADER_STAGE_COUNT); ++shader_stage)
{
executeIteration(static_cast<Utils::_shader_stage>(shader_stage));
deinitIteration();
} /* for (all shader stages) */
/* All done */
if (m_has_test_passed)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
return STOP;
}
/** Constructor
*
* @param context CTS context
**/
NegativeTest7::NegativeTest7(deqp::Context& context)
: TestCase(context, "recursion", "Verify that it is not possible to build program with recursing subroutines")
, m_program_id(0)
, m_vertex_shader_id(0)
{
/* Nothing to be done here */
}
/** Deinitializes all GL objects that may have been created during test execution
*
**/
void NegativeTest7::deinit()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
if (m_program_id != 0)
{
gl.deleteProgram(m_program_id);
m_program_id = 0;
}
if (m_vertex_shader_id != 0)
{
gl.deleteShader(m_vertex_shader_id);
m_vertex_shader_id = 0;
}
}
/** Executes test iteration.
*
* @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed.
**/
tcu::TestNode::IterateResult NegativeTest7::iterate()
{
static const GLchar* vertex_shader_with_static_recursion =
"#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"precision highp float;\n"
"\n"
"subroutine vec4 routine_type(in vec4 data, in uint control);\n"
"\n"
"subroutine (routine_type) vec4 power_routine(in vec4 data, in uint control)\n"
"{\n"
" if (0 != control)\n"
" {\n"
" return data * power_routine(data, control - 1);\n"
" }\n"
" else\n"
" {\n"
" return vec4(1, 1, 1, 1);\n"
" }\n"
"}\n"
"\n"
"subroutine (routine_type) vec4 select_routine(in vec4 data, in uint control)\n"
"{\n"
" if (0 == control)\n"
" {\n"
" return data.rrrr;\n"
" }\n"
" else if (1 == control)\n"
" {\n"
" return data.gggg;\n"
" }\n"
" else if (2 == control)\n"
" {\n"
" return data.bbbb;\n"
" }\n"
" else\n"
" {\n"
" return data.aaaa;\n"
" }\n"
"}\n"
"\n"
"subroutine uniform routine_type routine;\n"
"\n"
"uniform vec4 uni_value;\n"
"uniform uint uni_control;\n"
"\n"
"out vec4 out_result;\n"
"\n"
"void main()\n"
"{\n"
" out_result = routine(uni_value, uni_control);\n"
"}\n"
"\n";
static const GLchar* vertex_shader_with_dynamic_recursion =
"#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"precision highp float;\n"
"\n"
"subroutine vec4 routine_type(in vec4 data);\n"
"\n"
"subroutine uniform routine_type routine;\n"
"\n"
"subroutine (routine_type) vec4 div_by_2(in vec4 data)\n"
"{\n"
" return data / 2;\n"
"}\n"
"\n"
"subroutine (routine_type) vec4 div_routine_result_by_2(in vec4 data)\n"
"{\n"
" return routine(data) / 2;\n"
"}\n"
"\n"
"uniform vec4 uni_value;\n"
"\n"
"out vec4 out_result;\n"
"\n"
"void main()\n"
"{\n"
" out_result = routine(uni_value);\n"
"}\n"
"\n";
static const GLchar* vertex_shader_with_subroutine_function_recursion =
"#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"precision highp float;\n"
"\n"
"subroutine vec4 routine_type(in vec4 data);\n"
"\n"
"subroutine uniform routine_type routine;\n"
"\n"
"vec4 function(in vec4 data)\n"
"{\n"
" return routine(data) + vec4(0.5, 0.5, 0.5, 0.5);\n"
"}\n"
"\n"
"subroutine (routine_type) vec4 routine_a(in vec4 data)\n"
"{\n"
" return function(data) / 2;\n"
"}\n"
"\n"
"subroutine (routine_type) vec4 routine_b(in vec4 data)\n"
"{\n"
" return routine_a(data) * 2;\n"
"}\n"
"\n"
"uniform vec4 uni_value;\n"
"\n"
"out vec4 out_result;\n"
"\n"
"void main()\n"
"{\n"
" out_result = routine(uni_value);\n"
"}\n"
"\n";
bool result = true;
if (false == test(vertex_shader_with_subroutine_function_recursion, "routine_a"))
{
result = false;
}
if (false == test(vertex_shader_with_dynamic_recursion, "div_routine_result_by_2"))
{
result = false;
}
if (false == test(vertex_shader_with_static_recursion, "power_routine"))
{
result = false;
}
/* Set result */
if (true == result)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
/* Done */
return tcu::TestNode::STOP;
}
/** Try to build program from vertex shader code.
*
* @param vertex_shader_code Source code of vertex shader
* @param name_of_recursive_routine Name of subroutine that should cause link failure due to recursion
*
* @return true build process failed, false otherwise
**/
bool NegativeTest7::test(const GLchar* vertex_shader_code, const GLchar* name_of_recursive_routine)
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
bool result = true;
static const GLchar* varying_name = "out_result";
/* Try to build program */
if (true == Utils::buildProgram(gl, vertex_shader_code, "", "", "", "", &varying_name /* varying_names */,
1 /* n_varyings */, &m_vertex_shader_id, 0, 0, 0, 0, &m_program_id))
{
/* Success is considered an error */
Utils::program program(m_context);
GLuint index = 0;
program.build(0, 0, 0, 0, 0, vertex_shader_code, 0, 0);
/* Verify that recursive subroutine is active */
try
{
index = program.getSubroutineIndex(name_of_recursive_routine, GL_VERTEX_SHADER);
}
catch (const std::exception& exc)
{
/* Something wrong with shader or compilation */
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "It is expected that subroutine: \n"
<< name_of_recursive_routine
<< " is considered active. This subroutine is potentially recursive and should cause link failure."
<< tcu::TestLog::EndMessage;
throw exc;
}
/* Subsoutine is active, however linking should fail */
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "Error. Program with potentially recursive subroutine, "
<< name_of_recursive_routine << ", which is active, index: " << index << ", has been built successfully.\n"
<< vertex_shader_code << tcu::TestLog::EndMessage;
result = false;
}
/* Delete program and shader */
deinit();
/* Done */
return result;
}
/** Constructor.
*
* @param context Rendering context.
*
**/
NegativeTest8::NegativeTest8(deqp::Context& context)
: TestCase(context, "subroutine_wo_body", "Verifies that a compile- or link-time error occurs if a function "
"declared as a subroutine does not include a body.")
, m_fs_id(0)
, m_gs_id(0)
, m_has_test_passed(true)
, m_po_id(0)
, m_tc_id(0)
, m_te_id(0)
, m_vs_id(0)
{
/* Left blank intentionally */
}
/** Deinitializes all GL objects that may have been created during test execution */
void NegativeTest8::deinit()
{
deinitIteration();
}
/** Deinitializes all GL objects that may have been created during a single test
* iteration.
***/
void NegativeTest8::deinitIteration()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
if (m_fs_id != 0)
{
gl.deleteShader(m_fs_id);
m_fs_id = 0;
}
if (m_gs_id != 0)
{
gl.deleteShader(m_gs_id);
m_gs_id = 0;
}
if (m_po_id != 0)
{
gl.deleteProgram(m_po_id);
m_po_id = 0;
}
if (m_tc_id != 0)
{
gl.deleteShader(m_tc_id);
m_tc_id = 0;
}
if (m_te_id != 0)
{
gl.deleteShader(m_te_id);
m_te_id = 0;
}
if (m_vs_id != 0)
{
gl.deleteShader(m_vs_id);
m_vs_id = 0;
}
}
/** Executes a single test iteration.
*
* If the iteration fails, m_has_test_passed will be set to false.
*
* @param shader_stage Shader stage, for which two duplicate functions
* (one additionally marked as subroutine) should
* be defined.
**/
void NegativeTest8::executeIteration(const Utils::_shader_stage& shader_stage)
{
std::string fs_body = getFragmentShaderBody(shader_stage == Utils::SHADER_STAGE_FRAGMENT);
std::string gs_body = getGeometryShaderBody(shader_stage == Utils::SHADER_STAGE_GEOMETRY);
std::string tc_body = getTessellationControlShaderBody(shader_stage == Utils::SHADER_STAGE_TESSELLATION_CONTROL);
std::string te_body =
getTessellationEvaluationShaderBody(shader_stage == Utils::SHADER_STAGE_TESSELLATION_EVALUATION);
std::string vs_body = getVertexShaderBody(shader_stage == Utils::SHADER_STAGE_VERTEX);
if (Utils::buildProgram(m_context.getRenderContext().getFunctions(), vs_body, tc_body, te_body, gs_body, fs_body,
DE_NULL, /* xfb_varyings */
DE_NULL, /* n_xfb_varyings */
&m_vs_id, &m_tc_id, &m_te_id, &m_gs_id, &m_fs_id, &m_po_id))
{
/* None of the test programs should ever build successfully */
m_testCtx.getLog() << tcu::TestLog::Message
<< "A program object consisting of FS+GS+TC+TE+VS stages has linked successfully, "
"even though one of the shaders only defines a subroutine that lacks any body."
"\n"
"Vertex shader:\n"
"\n"
<< vs_body << "\n"
"Tessellation control shader:\n"
"\n"
<< tc_body << "\n"
"Tessellation evaluation shader:\n"
"\n"
<< te_body << "\n"
"Geometry shader:\n"
"\n"
<< gs_body << "\n"
"Fragment shader:\n"
"\n"
<< fs_body << tcu::TestLog::EndMessage;
m_has_test_passed = false;
}
}
/** Retrieves fragment shader body.
*
* @param include_invalid_declaration true if a subroutine prototype should be included in
* the shader, false to skip it.
*
* @return Requested string.
**/
std::string NegativeTest8::getFragmentShaderBody(bool include_invalid_declaration) const
{
std::stringstream result_sstream;
result_sstream << "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n";
if (include_invalid_declaration)
{
result_sstream << "subroutine void subroutineTestTypeFS(out vec4 test);\n"
"\n"
"subroutine(subroutineTestTypeFS) void test_impl1(out vec4 test);\n"
"\n"
"subroutine uniform subroutineTestTypeFS test_subroutineFS;\n";
}
result_sstream << "\n"
"out vec4 result;\n"
"\n"
"void main()\n"
"{\n";
if (include_invalid_declaration)
{
result_sstream << " test_subroutineFS(result);\n";
}
else
{
result_sstream << " result = vec4(0, 1, 2, 3);\n";
}
result_sstream << "}\n";
return result_sstream.str();
}
/** Retrieves geometry shader body.
*
* @param include_invalid_declaration true if a subroutine prototype should be included in
* the shader, false to skip it.
*
* @return Requested string.
**/
std::string NegativeTest8::getGeometryShaderBody(bool include_invalid_declaration) const
{
std::stringstream result_sstream;
result_sstream << "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"layout (points) in;\n"
"layout (points, max_vertices = 1) out;\n"
"\n";
if (include_invalid_declaration)
{
result_sstream << "subroutine void subroutineTestTypeGS(out vec4 test);\n"
"\n"
"subroutine(subroutineTestTypeGS) void test_impl1(out vec4 test);\n"
"\n"
"subroutine uniform subroutineTestTypeGS test_subroutineGS;\n";
}
result_sstream << "\n"
"void main()\n"
"{\n";
if (include_invalid_declaration)
{
result_sstream << " test_subroutineGS(gl_Position);\n";
}
else
{
result_sstream << " gl_Position = vec4(0, 1, 2, 3);\n";
}
result_sstream << "EmitVertex();\n"
"}\n";
return result_sstream.str();
}
/** Retrieves tessellation control shader body.
*
* @param include_invalid_declaration true if a subroutine prototype should be included in
* the shader, false to skip it.
*
* @return Requested string.
**/
std::string NegativeTest8::getTessellationControlShaderBody(bool include_invalid_declaration) const
{
std::stringstream result_sstream;
result_sstream << "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"layout (vertices = 4) out;\n"
"\n";
if (include_invalid_declaration)
{
result_sstream << "subroutine void subroutineTestTypeTC(out vec4 test);\n"
"\n"
"subroutine(subroutineTestTypeTC) void test_impl1(out vec4 test);\n"
"\n"
"subroutine uniform subroutineTestTypeTC test_subroutineTC;\n";
}
result_sstream << "\n"
"void main()\n"
"{\n";
if (include_invalid_declaration)
{
result_sstream << " test_subroutineTC(gl_out[gl_InvocationID].gl_Position);\n";
}
else
{
result_sstream << " gl_out[gl_InvocationID].gl_Position = vec4(0, 1, 2, 3);\n";
}
result_sstream << "}\n";
return result_sstream.str();
}
/** Retrieves tessellation evaluation body.
*
* @param include_invalid_declaration true if a subroutine prototype should be included in
* the shader, false to skip it.
*
* @return Requested string.
**/
std::string NegativeTest8::getTessellationEvaluationShaderBody(bool include_invalid_declaration) const
{
std::stringstream result_sstream;
result_sstream << "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"layout (quads) in;\n"
"\n";
if (include_invalid_declaration)
{
result_sstream << "subroutine void subroutineTestTypeTE(out vec4 test);\n"
"\n"
"subroutine(subroutineTestTypeTE) void test_impl1(out vec4 test);\n"
"\n"
"subroutine uniform subroutineTestTypeTE test_subroutineTE;\n";
}
result_sstream << "\n"
"void main()\n"
"{\n";
if (include_invalid_declaration)
{
result_sstream << " test_subroutineTE(gl_Position);\n";
}
else
{
result_sstream << " gl_Position = vec4(0, 1, 2, 3);\n";
}
result_sstream << "}\n";
return result_sstream.str();
}
/** Retrieves vertex shader body.
*
* @param include_invalid_declaration true if a subroutine prototype should be included in
* the shader, false to skip it.
*
* @return Requested string.
**/
std::string NegativeTest8::getVertexShaderBody(bool include_invalid_declaration) const
{
std::stringstream result_sstream;
result_sstream << "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n";
if (include_invalid_declaration)
{
result_sstream << "subroutine void subroutineTestTypeVS(out vec4 test);\n"
"\n"
"subroutine(subroutineTestTypeVS) void test_impl1(out vec4 test);\n"
"\n"
"subroutine uniform subroutineTestTypeVS test_subroutineVS;\n";
}
result_sstream << "\n"
"void main()\n"
"{\n";
if (include_invalid_declaration)
{
result_sstream << " test_subroutineVS(gl_Position);\n";
}
else
{
result_sstream << " gl_Position = vec4(0, 1, 2, 3);\n";
}
result_sstream << "}\n";
return result_sstream.str();
}
/** Executes test iteration.
*
* @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed.
*/
tcu::TestNode::IterateResult NegativeTest8::iterate()
{
/* Do not execute the test if GL_ARB_shader_subroutine is not supported */
if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_shader_subroutine"))
{
throw tcu::NotSupportedError("GL_ARB_shader_subroutine is not supported.");
}
/* Iterate over all shader stages. For each iteration, iteration-specific shader stage
* will feature an invalid subroutine definition. Other shader stages will be assigned
* valid bodies. The test fails if a program built of such shaders links successfully.
*/
for (int shader_stage = static_cast<int>(Utils::SHADER_STAGE_FIRST);
shader_stage < static_cast<int>(Utils::SHADER_STAGE_COUNT); ++shader_stage)
{
executeIteration(static_cast<Utils::_shader_stage>(shader_stage));
deinitIteration();
} /* for (all shader stages) */
/* All done */
if (m_has_test_passed)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
return STOP;
}
/** Constructor.
*
* @param context Rendering context.
**/
NegativeTest9::NegativeTest9(deqp::Context& context)
: TestCase(context, "subroutines_cannot_be_assigned_float_int_values_or_be_compared",
"Make sure it is not possible to assign float/int to subroutine "
"uniform and that subroutine uniform values cannot be compared.")
, m_has_test_passed(true)
, m_po_id(0)
, m_vs_id(0)
{
/* Left blank intentionally */
}
/** Deinitializes any GL objects that may have been created during
* test execution.
**/
void NegativeTest9::deinit()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
if (m_po_id != 0)
{
gl.deleteProgram(m_po_id);
m_po_id = 0;
}
if (m_vs_id != 0)
{
gl.deleteShader(m_vs_id);
m_vs_id = 0;
}
}
/** Returns a literal corresponding to user-specified test case enum.
*
* @param test_case As per description.
*
* @return Requested string.
**/
std::string NegativeTest9::getTestCaseString(const _test_case& test_case)
{
std::string result = "?";
switch (test_case)
{
case TEST_CASE_INVALID_FLOAT_TO_SUBROUTINE_UNIFORM_ASSIGNMENT:
result = "TEST_CASE_INVALID_FLOAT_TO_SUBROUTINE_UNIFORM_ASSIGNMENT";
break;
case TEST_CASE_INVALID_INT_TO_SUBROUTINE_UNIFORM_ASSIGNMENT:
result = "TEST_CASE_INVALID_INT_TO_SUBROUTINE_UNIFORM_ASSIGNMENT";
break;
case TEST_CASE_INVALID_SUBROUTINE_UNIFORM_VALUE_COMPARISON:
result = "TEST_CASE_INVALID_SUBROUTINE_UNIFORM_VALUE_COMPARISON";
break;
default:
break;
}
return result;
}
/** Retrieves vertex shader body for user-specified test case.
*
* @param test_case As per description.
*
* @return Requested string.
**/
std::string NegativeTest9::getVertexShader(const _test_case& test_case)
{
std::stringstream result_sstream;
/* Form pre-amble */
result_sstream << "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
/* Define a subroutine */
"subroutine void subroutineType(inout vec4 test);\n"
"\n"
"subroutine(subroutineType) void test_function(inout vec4 test)\n"
"{\n"
" test += vec4(0, 1, 2, 3);\n"
"}\n"
"\n"
"subroutine uniform subroutineType function;\n"
"\n";
/* Include case-specific implementation */
switch (test_case)
{
case TEST_CASE_INVALID_FLOAT_TO_SUBROUTINE_UNIFORM_ASSIGNMENT:
{
result_sstream << "void main()\n"
"{\n"
" function = 1.0f;\n"
"\n"
" function(gl_Position);\n"
"}\n";
break;
}
case TEST_CASE_INVALID_INT_TO_SUBROUTINE_UNIFORM_ASSIGNMENT:
{
result_sstream << "void main()\n"
"{\n"
" function = 1;\n"
"\n"
" function(gl_Position);\n"
"}\n";
break;
}
case TEST_CASE_INVALID_SUBROUTINE_UNIFORM_VALUE_COMPARISON:
{
result_sstream << "subroutine uniform subroutineType function2;\n"
"\n"
"void main()\n"
"{\n"
" if (function == function2)\n"
" {\n"
" function(gl_Position);\n"
" }\n"
" else\n"
" {\n"
" function2(gl_Position);\n"
" }\n"
"}\n";
break;
}
default:
break;
} /* switch (test_case) */
/* Done */
return result_sstream.str();
}
/** Executes test iteration.
*
* @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed.
*/
tcu::TestNode::IterateResult NegativeTest9::iterate()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Do not execute the test if GL_ARB_shader_subroutine is not supported */
if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_shader_subroutine"))
{
throw tcu::NotSupportedError("GL_ARB_shader_subroutine is not supported.");
}
/* Iterate over all test cases */
for (int test_case = static_cast<int>(TEST_CASE_FIRST); test_case != static_cast<int>(TEST_CASE_COUNT); ++test_case)
{
/* Try to build a program object using invalid vertex shader, specific to the
* iteration we're currently in */
std::string vs_body = getVertexShader(static_cast<_test_case>(test_case));
if (ShaderSubroutine::Utils::buildProgram(gl, vs_body, "", /* tc_body */
"", /* te_body */
"", /* gs_body */
"", /* fs_body */
DE_NULL, /* xfb_varyings */
0, /* n_xfb_varyings */
&m_vs_id, DE_NULL, /* out_tc_id */
DE_NULL, /* out_te_id */
DE_NULL, /* out_gs_id */
DE_NULL, /* out_fs_id */
&m_po_id))
{
m_testCtx.getLog() << tcu::TestLog::Message << "A program object was successfully built for ["
<< getTestCaseString(static_cast<_test_case>(test_case))
<< "] test case, even though it was invalid." << tcu::TestLog::EndMessage;
m_has_test_passed = false;
}
/* Delete any objects that may have been created */
deinit();
} /* for (all test cases) */
/** All done */
if (m_has_test_passed)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
return STOP;
}
/** Constructor.
*
* @param context Rendering context.
**/
NegativeTest10::NegativeTest10(deqp::Context& context)
: TestCase(context, "function_overloading_forbidden_for_subroutines",
"Check that an overloaded function cannot be declared with subroutine and "
"a program will fail to compile or link if any shader or stage contains"
" two or more functions with the same name if the name is associated with"
" a subroutine type.")
, m_has_test_passed(true)
, m_fs_id(0)
, m_gs_id(0)
, m_po_id(0)
, m_tc_id(0)
, m_te_id(0)
, m_vs_id(0)
{
/* Left blank intentionally */
}
/** Deinitializes any GL objects that may have been created during
* test execution.
**/
void NegativeTest10::deinit()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
if (m_fs_id != 0)
{
gl.deleteShader(m_fs_id);
m_fs_id = 0;
}
if (m_gs_id != 0)
{
gl.deleteShader(m_gs_id);
m_gs_id = 0;
}
if (m_po_id != 0)
{
gl.deleteProgram(m_po_id);
m_po_id = 0;
}
if (m_tc_id != 0)
{
gl.deleteShader(m_tc_id);
m_tc_id = 0;
}
if (m_te_id != 0)
{
gl.deleteShader(m_te_id);
m_te_id = 0;
}
if (m_vs_id != 0)
{
gl.deleteShader(m_vs_id);
m_vs_id = 0;
}
}
/** Retrieves fragment shader that should be used for the purpose of the test.
* An overloaded version of a subroutine function is inserted if
* @param include_duplicate_function flag is set to true.
*
* @param include_duplicate_function As per description.
*
* @return Requested string.
**/
std::string NegativeTest10::getFragmentShader(bool include_duplicate_function)
{
std::stringstream result_sstream;
result_sstream << "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"subroutine void subroutineType(inout vec4 test);\n"
"\n"
"subroutine(subroutineType) void test_function(inout vec4 test)\n"
"{\n"
" test = vec4(2, 3, 4, 5);\n"
"}\n"
"\n"
"subroutine uniform subroutineType function;\n"
"out vec4 result;\n"
"\n";
if (include_duplicate_function)
{
result_sstream << "void test_function(inout vec4 test)\n"
"{\n"
" test = vec4(3, 4, 5, 6);\n"
"}\n"
"\n";
}
result_sstream << "void main()\n"
"{\n"
" test_function(result);\n"
"}\n";
return result_sstream.str();
}
/** Retrieves geometry shader that should be used for the purpose of the test.
* An overloaded version of a subroutine function is inserted if
* @param include_duplicate_function flag is set to true.
*
* @param include_duplicate_function As per description.
*
* @return Requested string.
**/
std::string NegativeTest10::getGeometryShader(bool include_duplicate_function)
{
std::stringstream result_sstream;
result_sstream << "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"layout (triangles) in;\n"
"layout (triangle_strip, max_vertices = 4) out;\n"
"\n"
"subroutine void subroutineType(inout vec4 test);\n"
"\n"
"subroutine(subroutineType) void test_function(inout vec4 test)\n"
"{\n"
" test = vec4(2, 3, 4, 5);\n"
"}\n"
"\n"
"subroutine uniform subroutineType function;\n"
"\n";
if (include_duplicate_function)
{
result_sstream << "void test_function(inout vec4 test)\n"
"{\n"
" test = vec4(3, 4, 5, 6);\n"
"}\n"
"\n";
}
result_sstream << "void main()\n"
"{\n"
" function(gl_Position);\n"
" EmitVertex();\n"
" EndPrimitive();\n"
"}\n";
return result_sstream.str();
}
/** Retrieves tess control shader that should be used for the purpose of the test.
* An overloaded version of a subroutine function is inserted if
* @param include_duplicate_function flag is set to true.
*
* @param include_duplicate_function As per description.
*
* @return Requested string.
**/
std::string NegativeTest10::getTessellationControlShader(bool include_duplicate_function)
{
std::stringstream result_sstream;
result_sstream << "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"layout (vertices = 4) out;\n"
"\n"
"subroutine void subroutineType(inout vec4 test);\n"
"\n"
"subroutine(subroutineType) void test_function(inout vec4 test)\n"
"{\n"
" test = vec4(2, 3, 4, 5);\n"
"}\n"
"\n"
"subroutine uniform subroutineType function;\n"
"\n";
if (include_duplicate_function)
{
result_sstream << "void test_function(inout vec4 test)\n"
"{\n"
" test = vec4(3, 4, 5, 6);\n"
"}\n"
"\n";
}
result_sstream << "void main()\n"
"{\n"
" vec4 temp;\n"
"\n"
" function(temp);\n"
"\n"
" gl_out[gl_InvocationID].gl_Position = temp;\n"
" gl_TessLevelInner[0] = temp.x;\n"
" gl_TessLevelInner[1] = temp.y;\n"
" gl_TessLevelOuter[0] = temp.z;\n"
" gl_TessLevelOuter[1] = temp.w;\n"
" gl_TessLevelOuter[2] = temp.x;\n"
" gl_TessLevelOuter[3] = temp.y;\n"
"}\n";
return result_sstream.str();
}
/** Retrieves tess evaluation shader that should be used for the purpose of the test.
* An overloaded version of a subroutine function is inserted if
* @param include_duplicate_function flag is set to true.
*
* @param include_duplicate_function As per description.
*
* @return Requested string.
**/
std::string NegativeTest10::getTessellationEvaluationShader(bool include_duplicate_function)
{
std::stringstream result_sstream;
result_sstream << "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"layout (quads) in;\n"
"\n"
"subroutine void subroutineType(inout vec4 test);\n"
"\n"
"subroutine(subroutineType) void test_function(inout vec4 test)\n"
"{\n"
" test = vec4(2, 3, 4, 5);\n"
"}\n"
"\n"
"subroutine uniform subroutineType function;\n"
"\n";
if (include_duplicate_function)
{
result_sstream << "void test_function(inout vec4 test)\n"
"{\n"
" test = vec4(3, 4, 5, 6);\n"
"}\n"
"\n";
}
result_sstream << "void main()\n"
"{\n"
" vec4 temp;\n"
"\n"
" function(temp);\n"
"\n"
" gl_Position = temp;\n"
"}\n";
return result_sstream.str();
}
/** Retrieves vertex shader that should be used for the purpose of the test.
* An overloaded version of a subroutine function is inserted if
* @param include_duplicate_function flag is set to true.
*
* @param include_duplicate_function As per description.
*
* @return Requested string.
**/
std::string NegativeTest10::getVertexShader(bool include_duplicate_function)
{
std::stringstream result_sstream;
result_sstream << "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"subroutine void subroutineType(inout vec4 test);\n"
"\n"
"subroutine(subroutineType) void test_function(inout vec4 test)\n"
"{\n"
" test = vec4(2, 3, 4, 5);\n"
"}\n"
"\n"
"subroutine uniform subroutineType function;\n"
"\n";
if (include_duplicate_function)
{
result_sstream << "void test_function(inout vec4 test)\n"
"{\n"
" test = vec4(3, 4, 5, 6);\n"
"}\n"
"\n";
}
result_sstream << "void main()\n"
"{\n"
" function(gl_Position);\n"
"}\n";
return result_sstream.str();
}
/** Fills m_test_cases field with test case descriptors */
void NegativeTest10::initTestCases()
{
/* For each test case, only one shader stage should define a function that
* has already been defined as a subroutine. */
for (int offending_shader_stage_it = static_cast<int>(Utils::SHADER_STAGE_FIRST);
offending_shader_stage_it != static_cast<int>(Utils::SHADER_STAGE_COUNT); ++offending_shader_stage_it)
{
Utils::_shader_stage offending_shader_stage = static_cast<Utils::_shader_stage>(offending_shader_stage_it);
/* Form the test case descriptor */
std::stringstream name_sstream;
_test_case test_case;
name_sstream << "Broken shader stage:" << Utils::getShaderStageString(offending_shader_stage);
test_case.fs_body = getFragmentShader(offending_shader_stage == Utils::SHADER_STAGE_FRAGMENT);
test_case.gs_body = getGeometryShader(offending_shader_stage == Utils::SHADER_STAGE_GEOMETRY);
test_case.name = name_sstream.str();
test_case.tc_body =
getTessellationControlShader(offending_shader_stage == Utils::SHADER_STAGE_TESSELLATION_CONTROL);
test_case.te_body =
getTessellationEvaluationShader(offending_shader_stage == Utils::SHADER_STAGE_TESSELLATION_EVALUATION);
test_case.vs_body = getVertexShader(offending_shader_stage == Utils::SHADER_STAGE_VERTEX);
m_test_cases.push_back(test_case);
}
}
/** Executes test iteration.
*
* @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed.
*/
tcu::TestNode::IterateResult NegativeTest10::iterate()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Do not execute the test if GL_ARB_shader_subroutine is not supported */
if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_shader_subroutine"))
{
throw tcu::NotSupportedError("GL_ARB_shader_subroutine is not supported.");
}
/* Form test cases */
initTestCases();
/* Iterate over all test cases */
for (_test_cases_const_iterator test_case_iterator = m_test_cases.begin(); test_case_iterator != m_test_cases.end();
++test_case_iterator)
{
const _test_case& test_case = *test_case_iterator;
/* Try to build the program object */
if (ShaderSubroutine::Utils::buildProgram(gl, test_case.vs_body, test_case.tc_body, test_case.te_body,
test_case.gs_body, test_case.fs_body, DE_NULL, /* xfb_varyings */
0, /* n_xfb_varyings */
&m_vs_id, (test_case.tc_body.length() > 0) ? &m_tc_id : DE_NULL,
(test_case.te_body.length() > 0) ? &m_te_id : DE_NULL,
(test_case.gs_body.length() > 0) ? &m_gs_id : DE_NULL,
(test_case.fs_body.length() > 0) ? &m_fs_id : DE_NULL, &m_po_id))
{
m_testCtx.getLog() << tcu::TestLog::Message << "A program object was successfully built for ["
<< test_case.name << "] test case, even though it was invalid."
<< tcu::TestLog::EndMessage;
m_has_test_passed = false;
}
/* Delete any objects that may have been created */
deinit();
} /* for (all test cases) */
/** All done */
if (m_has_test_passed)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
return STOP;
}
/** Constructor.
*
* @param context Rendering context.
**/
NegativeTest11::NegativeTest11(deqp::Context& context)
: TestCase(context, "subroutine_uniforms_used_for_sampling_atomic_image_functions",
"Tries to use subroutine uniforms in invalid way in sampling, "
"atomic and image functions. Verifies that compile- or link-time "
"error occurs.")
, m_has_test_passed(true)
, m_po_id(0)
, m_vs_id(0)
{
/* Left blank intentionally */
}
/** Deinitializes any GL objects that may have been created during
* test execution.
**/
void NegativeTest11::deinit()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
if (m_po_id != 0)
{
gl.deleteProgram(m_po_id);
m_po_id = 0;
}
if (m_vs_id != 0)
{
gl.deleteShader(m_vs_id);
m_vs_id = 0;
}
}
/** Returns a literal corresponding to user-specified test case enum.
*
* @param test_case As per description.
*
* @return Requested string.
**/
std::string NegativeTest11::getTestCaseString(const _test_case& test_case)
{
std::string result = "?";
switch (test_case)
{
case TEST_CASE_INVALID_TEXTURE_SAMPLING_ATTEMPT:
result = "TEST_CASE_INVALID_TEXTURE_SAMPLING_ATTEMPT";
break;
case TEST_CASE_INVALID_ATOMIC_COUNTER_USAGE_ATTEMPT:
result = "TEST_CASE_INVALID_ATOMIC_COUNTER_USAGE_ATTEMPT";
break;
case TEST_CASE_INVALID_IMAGE_FUNCTION_USAGE_ATTEMPT:
result = "TEST_CASE_INVALID_IMAGE_FUNCTION_USAGE_ATTEMPT";
break;
default:
break;
}
return result;
}
/** Retrieves vertex shader body for user-specified test case.
*
* @param test_case As per description.
*
* @return Requested string.
**/
std::string NegativeTest11::getVertexShader(const _test_case& test_case)
{
std::stringstream result_sstream;
/* Form pre-amble */
result_sstream << "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n";
if (test_case == TEST_CASE_INVALID_ATOMIC_COUNTER_USAGE_ATTEMPT)
{
result_sstream << "#extension GL_ARB_shader_atomic_counters : require\n";
}
result_sstream << "\n"
/* Define a subroutine */
"subroutine void subroutineType(inout vec4 test);\n"
"\n"
"subroutine(subroutineType) void test_function(inout vec4 test)\n"
"{\n"
" test += vec4(0, 1, 2, 3);\n"
"}\n"
"\n"
"subroutine uniform subroutineType function;\n"
"\n"
/* Define main() body */
"void main()\n"
"{\n";
/* Implement case-specific behavior */
switch (test_case)
{
case TEST_CASE_INVALID_ATOMIC_COUNTER_USAGE_ATTEMPT:
{
result_sstream << "if (atomicCounter(function) > 2)\n"
"{\n"
" gl_Position = vec4(1);\n"
"}\n";
break;
}
case TEST_CASE_INVALID_IMAGE_FUNCTION_USAGE_ATTEMPT:
{
result_sstream << "imageStore(function, vec2(0.0, 1.0), vec4(1.0) );\n";
break;
}
case TEST_CASE_INVALID_TEXTURE_SAMPLING_ATTEMPT:
{
result_sstream << "gl_Position = texture(function, vec2(1.0) );\n";
break;
}
default:
break;
} /* switch (test_case) */
/* Close main() body */
result_sstream << "}\n";
/* Done */
return result_sstream.str();
}
/** Executes test iteration.
*
* @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed.
*/
tcu::TestNode::IterateResult NegativeTest11::iterate()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Do not execute the test if GL_ARB_shader_subroutine is not supported */
if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_shader_subroutine"))
{
throw tcu::NotSupportedError("GL_ARB_shader_subroutine is not supported.");
}
/* Iterate over all test cases */
for (int test_case = static_cast<int>(TEST_CASE_FIRST); test_case != static_cast<int>(TEST_CASE_COUNT); ++test_case)
{
if (static_cast<_test_case>(test_case) == TEST_CASE_INVALID_ATOMIC_COUNTER_USAGE_ATTEMPT &&
!m_context.getContextInfo().isExtensionSupported("GL_ARB_shader_atomic_counters"))
{
/* This iteration requires atomic counter support that this GL implementation
* is not capable of. Skip the iteration
*/
continue;
}
/* Try to build a program object using invalid vertex shader, specific to the
* iteration we're currently in */
std::string vs_body = getVertexShader(static_cast<_test_case>(test_case));
if (ShaderSubroutine::Utils::buildProgram(gl, vs_body, "", /* tc_body */
"", /* te_body */
"", /* gs_body */
"", /* fs_body */
DE_NULL, /* xfb_varyings */
0, /* n_xfb_varyings */
&m_vs_id, DE_NULL, /* out_tc_id */
DE_NULL, /* out_te_id */
DE_NULL, /* out_gs_id */
DE_NULL, /* out_fs_id */
&m_po_id))
{
m_testCtx.getLog() << tcu::TestLog::Message << "A program object was successfully built for ["
<< getTestCaseString(static_cast<_test_case>(test_case))
<< "] test case, even though it was invalid." << tcu::TestLog::EndMessage;
m_has_test_passed = false;
}
/* Delete any objects that may have been created */
deinit();
} /* for (all test cases) */
/** All done */
if (m_has_test_passed)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
return STOP;
}
/** Constructor.
*
* @param context Rendering context.
**/
NegativeTest12::NegativeTest12(deqp::Context& context)
: TestCase(context, "subroutines_not_allowed_as_variables_constructors_and_argument_or_return_types",
"Verifies that it is not allowed to use subroutine type for "
"local/global variables, constructors or argument/return type.")
, m_has_test_passed(true)
, m_po_id(0)
, m_vs_id(0)
{
/* Left blank intentionally */
}
/** Deinitializes any GL objects that may have been created during
* test execution.
**/
void NegativeTest12::deinit()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
if (m_po_id != 0)
{
gl.deleteProgram(m_po_id);
m_po_id = 0;
}
if (m_vs_id != 0)
{
gl.deleteShader(m_vs_id);
m_vs_id = 0;
}
}
/** Returns a literal corresponding to user-specified test case enum.
*
* @param test_case As per description.
*
* @return Requested string.
**/
std::string NegativeTest12::getTestCaseString(const _test_case& test_case)
{
std::string result = "?";
switch (test_case)
{
case TEST_CASE_INVALID_LOCAL_SUBROUTINE_VARIABLE:
result = "TEST_CASE_INVALID_LOCAL_SUBROUTINE_VARIABLE";
break;
case TEST_CASE_INVALID_GLOBAL_SUBROUTINE_VARIABLE:
result = "TEST_CASE_INVALID_GLOBAL_SUBROUTINE_VARIABLE";
break;
case TEST_CASE_SUBROUTINE_USED_AS_CONSTRUCTOR:
result = "TEST_CASE_SUBROUTINE_USED_AS_CONSTRUCTOR";
break;
case TEST_CASE_SUBROUTINE_USED_AS_CONSTRUCTOR_ARGUMENT:
result = "TEST_CASE_SUBROUTINE_USED_AS_CONSTRUCTOR_ARGUMENT";
break;
case TEST_CASE_SUBROUTINE_USED_AS_CONSTRUCTOR_RETURN_TYPE:
result = "TEST_CASE_SUBROUTINE_USED_AS_CONSTRUCTOR_RETURN_TYPE";
break;
default:
break;
}
return result;
}
/** Retrieves vertex shader body for user-specified test case.
*
* @param test_case As per description.
*
* @return Requested string.
**/
std::string NegativeTest12::getVertexShader(const _test_case& test_case)
{
std::stringstream result_sstream;
/* Form pre-amble */
result_sstream << "#version 400\n"
"\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
/* Define a subroutine */
"subroutine void subroutineType(inout vec4 test);\n"
"\n"
"subroutine(subroutineType) void test_function(inout vec4 test)\n"
"{\n"
" test += vec4(0, 1, 2, 3);\n"
"}\n"
"\n"
"subroutine uniform subroutineType function;\n"
"\n";
/* Include case-specific implementation */
switch (test_case)
{
case TEST_CASE_INVALID_LOCAL_SUBROUTINE_VARIABLE:
{
result_sstream << "void main()\n"
"{\n"
" subroutine subroutineType function2;\n"
" vec4 result;\n"
"\n"
" function2(result);\n"
" gl_Position = result;\n"
"}\n";
break;
}
case TEST_CASE_INVALID_GLOBAL_SUBROUTINE_VARIABLE:
{
result_sstream << "subroutine subroutineType function2;\n"
"\n"
"void main()\n"
"{\n"
" vec4 result;\n"
"\n"
" function2(result);\n"
" gl_Position = result;\n"
"}\n";
break;
}
case TEST_CASE_SUBROUTINE_USED_AS_CONSTRUCTOR:
{
result_sstream << "void main()\n"
"{\n"
" subroutineType(function);\n"
"}\n";
break;
}
case TEST_CASE_SUBROUTINE_USED_AS_CONSTRUCTOR_ARGUMENT:
{
result_sstream << "vec4 test_function(subroutineType argument)\n"
"{\n"
" vec4 result = vec4(1, 2, 3, 4);\n"
"\n"
" argument(result);\n"
"\n"
" return result;\n"
"}\n"
"\n"
"void main()\n"
"{\n"
" test_function(function);\n"
"}\n";
break;
}
case TEST_CASE_SUBROUTINE_USED_AS_CONSTRUCTOR_RETURN_TYPE:
{
result_sstream << "subroutineType test_function()\n"
"{\n"
" return function;\n"
"}\n"
"\n"
"void main()\n"
"{\n"
" test_function()(gl_Position);\n"
"}\n";
break;
}
default:
break;
} /* switch (test_case) */
/* Done */
return result_sstream.str();
}
/** Executes test iteration.
*
* @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed.
*/
tcu::TestNode::IterateResult NegativeTest12::iterate()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Do not execute the test if GL_ARB_shader_subroutine is not supported */
if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_shader_subroutine"))
{
throw tcu::NotSupportedError("GL_ARB_shader_subroutine is not supported.");
}
/* Iterate over all test cases */
for (int test_case = static_cast<int>(TEST_CASE_FIRST); test_case != static_cast<int>(TEST_CASE_COUNT); ++test_case)
{
/* Try to build a program object using invalid vertex shader, specific to the
* iteration we're currently in */
std::string vs_body = getVertexShader(static_cast<_test_case>(test_case));
if (ShaderSubroutine::Utils::buildProgram(gl, vs_body, "", /* tc_body */
"", /* te_body */
"", /* gs_body */
"", /* fs_body */
DE_NULL, /* xfb_varyings */
0, /* n_xfb_varyings */
&m_vs_id, DE_NULL, /* out_tc_id */
DE_NULL, /* out_te_id */
DE_NULL, /* out_gs_id */
DE_NULL, /* out_fs_id */
&m_po_id))
{
m_testCtx.getLog() << tcu::TestLog::Message << "A program object was successfully built for ["
<< getTestCaseString(static_cast<_test_case>(test_case))
<< "] test case, even though it was invalid." << tcu::TestLog::EndMessage;
m_has_test_passed = false;
}
/* Delete any objects that may have been created */
deinit();
} /* for (all test cases) */
/** All done */
if (m_has_test_passed)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
return STOP;
}
} /* ShaderSubroutine */
/** Constructor.
*
* @param context Rendering context.
**/
ShaderSubroutineTests::ShaderSubroutineTests(deqp::Context& context)
: TestCaseGroup(context, "shader_subroutine", "Verifies \"shader_subroutine\" functionality")
{
/* Left blank on purpose */
}
/** Initializes a texture_storage_multisample test group.
*
**/
void ShaderSubroutineTests::init(void)
{
addChild(new ShaderSubroutine::APITest1(m_context));
addChild(new ShaderSubroutine::APITest2(m_context));
addChild(new ShaderSubroutine::FunctionalTest1_2(m_context));
addChild(new ShaderSubroutine::FunctionalTest3_4(m_context));
addChild(new ShaderSubroutine::FunctionalTest5(m_context));
addChild(new ShaderSubroutine::FunctionalTest6(m_context));
addChild(new ShaderSubroutine::FunctionalTest7_8(m_context));
addChild(new ShaderSubroutine::FunctionalTest9(m_context));
addChild(new ShaderSubroutine::FunctionalTest10(m_context));
addChild(new ShaderSubroutine::FunctionalTest11(m_context));
addChild(new ShaderSubroutine::FunctionalTest12(m_context));
addChild(new ShaderSubroutine::FunctionalTest13(m_context));
addChild(new ShaderSubroutine::FunctionalTest14_15(m_context));
addChild(new ShaderSubroutine::FunctionalTest16(m_context));
addChild(new ShaderSubroutine::FunctionalTest17(m_context));
addChild(new ShaderSubroutine::FunctionalTest18_19(m_context));
addChild(new ShaderSubroutine::NegativeTest1(m_context));
addChild(new ShaderSubroutine::NegativeTest2(m_context));
addChild(new ShaderSubroutine::NegativeTest3(m_context));
addChild(new ShaderSubroutine::NegativeTest4(m_context));
addChild(new ShaderSubroutine::NegativeTest5(m_context));
addChild(new ShaderSubroutine::NegativeTest6(m_context));
addChild(new ShaderSubroutine::NegativeTest7(m_context));
addChild(new ShaderSubroutine::NegativeTest8(m_context));
addChild(new ShaderSubroutine::NegativeTest9(m_context));
addChild(new ShaderSubroutine::NegativeTest10(m_context));
addChild(new ShaderSubroutine::NegativeTest11(m_context));
addChild(new ShaderSubroutine::NegativeTest12(m_context));
}
} /* glcts namespace */