/*------------------------------------------------------------------------- * 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 #include #include 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 parameter * @param index parameter * @param offset parameter * @param 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 parameter * @param size parameter * @param data parameter * @param 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 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 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 data **/ void Utils::program::createFromBinary(const std::vector& 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& 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 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 // If 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(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 */, ¤t_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 inverse_order_negate_data[5] = { Utils::vec4(-2.0f, -1.0f, 1.0f, 2.0f), Utils::vec4(2.0f, 1.0f, -1.0f, -2.0f), Utils::vec4(2.0f, 1.0f, -1.0f, -2.0f), Utils::vec4(-2.0f, -1.0f, 1.0f, 2.0f), Utils::vec4(-2.0f, -1.0f, 1.0f, 2.0f), }; static const Utils::vec4 inverse_order_inverse_data[5] = { Utils::vec4(-2.0f, -1.0f, 1.0f, 2.0f), Utils::vec4(2.0f, 1.0f, -1.0f, -2.0f), Utils::vec4(-0.5f, -1.0f, 1.0f, 0.5f), Utils::vec4(0.5f, 1.0f, -1.0f, -0.5f), Utils::vec4(0.5f, 1.0f, -1.0f, -0.5f), }; static const Utils::vec4 inverse_order_square_data[5] = { Utils::vec4(-2.0f, -1.0f, 1.0f, 2.0f), Utils::vec4(2.0f, 1.0f, -1.0f, -2.0f), Utils::vec4(4.0f, 1.0f, 1.0f, 4.0f), Utils::vec4(4.0f, 1.0f, 1.0f, 4.0f), Utils::vec4(4.0f, 1.0f, 1.0f, 4.0f), }; static const Utils::vec4 negate_inverse_data[5] = { Utils::vec4(-2.0f, -1.0f, 1.0f, 2.0f), Utils::vec4(2.0f, 1.0f, -1.0f, -2.0f), Utils::vec4(-0.5f, -1.0f, 1.0f, 0.5f), Utils::vec4(0.5f, 1.0f, -1.0f, -0.5f), Utils::vec4(0.5f, 1.0f, -1.0f, -0.5f), }; static const Utils::vec4 negate_square_data[5] = { Utils::vec4(-2.0f, -1.0f, 1.0f, 2.0f), Utils::vec4(2.0f, 1.0f, -1.0f, -2.0f), Utils::vec4(4.0f, 1.0f, 1.0f, 4.0f), Utils::vec4(4.0f, 1.0f, 1.0f, 4.0f), Utils::vec4(-4.0f, -1.0f, -1.0f, -4.0f), }; static const Utils::vec4 inverse_square_data[5] = { Utils::vec4(-2.0f, -1.0f, 1.0f, 2.0f), Utils::vec4(-0.5f, -1.0f, 1.0f, 0.5f), Utils::vec4(4.0f, 1.0f, 1.0f, 4.0f), Utils::vec4(0.25f, 1.0f, 1.0f, 0.25f), Utils::vec4(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 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 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 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 parameter for getActiveSubroutineUniformiv * @param 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 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 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 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 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 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 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 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 input_data[3] = { Utils::vec4(1.0f, 4.0f, 9.0f, 16.0f), Utils::vec4(16.0f, 9.0f, 4.0f, 1.0f), Utils::vec4(0.25f, 0.5f, 0.75f, 1.0f) }; static const Utils::vec4 expected_result_from_first_routine[2] = { Utils::vec4(17.0f, 13.0f, 13.0f, 17.0f), Utils::vec4(-15.0f, -5.0f, 5.0f, 15.0f) }; static const Utils::vec4 expected_result_from_second_routine[2] = { Utils::vec4(1.0f, 16.0f, 81.0f, 256.0f), Utils::vec4(1.0f, 2.0f, 3.0f, 4.0f) }; static const Utils::vec4 expected_result_from_third_routine[2] = { Utils::vec4(16.25f, 36.5f, 36.75f, 17.0f), Utils::vec4(12.25f, 6.5f, 7.75f, 16.0f) }; static const Utils::vec4 expected_result_from_fourth_routine[2] = { Utils::vec4(0, 0, 0, 0), Utils::vec4(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 first_routine_result; Utils::vec4 second_routine_result; Utils::vec4 third_routine_result; Utils::vec4 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 input_data[3], const Utils::vec4& first_routine_result, const Utils::vec4& second_routine_result, const Utils::vec4& third_routine_result, const Utils::vec4& fourth_routine_result, const Utils::vec4& first_routine_expected_result, const Utils::vec4& second_routine_expected_result, const Utils::vec4& third_routine_expected_result, const Utils::vec4& 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 input_data[3], Utils::vec4& out_first_routine_result, Utils::vec4& out_second_routine_result, Utils::vec4& out_third_routine_result, Utils::vec4& 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& first_routine_result, const Utils::vec4& second_routine_result, const Utils::vec4& third_routine_result, const Utils::vec4& fourth_routine_result, const Utils::vec4& first_routine_expected_result, const Utils::vec4& second_routine_expected_result, const Utils::vec4& third_routine_expected_result, const Utils::vec4& 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 input_data(1.0f, 4.0f, 9.0f, 16.0f); static const Utils::vec4 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 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 uni_left(-1.0f, 0.75f, -0.5f, 0.25f); static const Utils::vec4 uni_right(1.0f, -0.75f, 0.5f, -0.25f); static const Utils::vec4 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& left, const Utils::vec4& right, Utils::vec4& 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& left, const Utils::vec4& right, const Utils::vec4& indices, Utils::vec4& out_combined, Utils::vec4& out_combined_inverted, Utils::vec4& out_constant, Utils::vec4& out_constant_inverted, Utils::vec4& out_dynamic, Utils::vec4& out_dynamic_inverted, Utils::vec4& 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 constant_values[] = { Utils::vec4(1, 2, 3, 4), Utils::vec4(-5, -6, -7, -8), Utils::vec4(-1, -2, -3, -4), Utils::vec4(5, 6, 7, 8), Utils::vec4(1, 2, 3, 4) }; /* Start values */ Utils::vec4 combined = left; Utils::vec4 combined_inverted = left; Utils::vec4 constant = constant_values[0]; Utils::vec4 constant_inverted = constant_values[0]; Utils::vec4 dynamic = left; Utils::vec4 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& left, const Utils::vec4& right, const Utils::vec4& indices, const Utils::vec4 vec4_expected[7], const Utils::vec4 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 constant_values[] = { Utils::vec4(1, 2, 3, 4), Utils::vec4(-5, -6, -7, -8), Utils::vec4(-1, -2, -3, -4), Utils::vec4(5, 6, 7, 8), Utils::vec4(1, 2, 3, 4) }; /* Values used by non-"constant" operations */ Utils::vec4 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* 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& left, const Utils::vec4& right, const Utils::vec4& 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 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 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(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 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 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 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 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 uni_input[] = { Utils::vec4(8, 64, 4096, 16777216), Utils::vec4(8, 64, 4096, 16777216) }; static const Utils::vec4 out_routine_1[] = { Utils::vec4(16777216, 4096, 64, 8), Utils::vec4(9, 65, 4097, 16777217) }; static const Utils::vec4 out_routine_2[] = { Utils::vec4(4, 32, 2048, 8388608), Utils::vec4(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 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& uni_input, const Utils::vec4 expected_routine_1_result[2], const Utils::vec4 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 routine_1_result; Utils::vec4 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& uni_input, const Utils::vec4& expected_routine_1_result, const Utils::vec4& 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 routine_1_result; Utils::vec4 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, ¤t_item.stage.default_subroutine1_value); gl.getUniformSubroutineuiv(current_item.so_type, current_item.stage.subroutine2_uniform_location, ¤t_item.stage.default_subroutine2_value); gl.getUniformSubroutineuiv(current_item.so_type, current_item.stage.subroutine3_uniform_location, ¤t_item.stage.default_subroutine3_value); gl.getUniformSubroutineuiv(current_item.so_type, current_item.stage.subroutine4_uniform_location, ¤t_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(TEST_CASE_FIRST); test_case != static_cast(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(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 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 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 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 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 * 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 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 * is not equal to the value of ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS for the * shader stage . */ 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 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 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 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 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 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 * 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 . */ 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(Utils::SHADER_STAGE_FIRST); referencing_stage < static_cast(Utils::SHADER_STAGE_COUNT); ++referencing_stage) { executeTestCase(static_cast(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(Utils::SHADER_STAGE_FIRST); shader_stage < static_cast(Utils::SHADER_STAGE_COUNT); ++shader_stage) { executeTest(static_cast(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(Utils::SHADER_STAGE_FIRST); shader_stage != static_cast(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(shader_stage)); for (unsigned int n_subroutine_types = 1; n_subroutine_types < 6; /* arbitrary number */ ++n_subroutine_types) { for (int test_case = static_cast(TEST_CASE_FIRST); test_case != static_cast(TEST_CASE_COUNT); ++test_case) { std::string body; const char* body_raw_ptr = NULL; glw::GLint compile_status = GL_FALSE; body = getShaderBody(static_cast(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(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(Utils::SHADER_STAGE_FIRST); shader_stage < static_cast(Utils::SHADER_STAGE_COUNT); ++shader_stage) { executeIteration(static_cast(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(Utils::SHADER_STAGE_FIRST); shader_stage < static_cast(Utils::SHADER_STAGE_COUNT); ++shader_stage) { executeIteration(static_cast(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(Utils::SHADER_STAGE_FIRST); shader_stage < static_cast(Utils::SHADER_STAGE_COUNT); ++shader_stage) { executeIteration(static_cast(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(TEST_CASE_FIRST); test_case != static_cast(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(Utils::SHADER_STAGE_FIRST); offending_shader_stage_it != static_cast(Utils::SHADER_STAGE_COUNT); ++offending_shader_stage_it) { Utils::_shader_stage offending_shader_stage = static_cast(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(TEST_CASE_FIRST); test_case != static_cast(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(TEST_CASE_FIRST); test_case != static_cast(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 */