| /*------------------------------------------------------------------------- |
| * OpenGL Conformance Test Suite |
| * ----------------------------- |
| * |
| * Copyright (c) 2014-2016 The Khronos Group Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ /*! |
| * \file |
| * \brief |
| */ /*-------------------------------------------------------------------*/ |
| |
| /** |
| * \file gl4cShaderSubroutineTests.cpp |
| * \brief Implements conformance tests for "Shader Subroutine" functionality. |
| */ /*-------------------------------------------------------------------*/ |
| |
| #include "gl4cShaderSubroutineTests.hpp" |
| #include "gluContextInfo.hpp" |
| #include "glwEnums.hpp" |
| #include "glwFunctions.hpp" |
| #include "tcuMatrix.hpp" |
| #include <cmath> |
| #include <cstring> |
| #include <deMath.h> |
| |
| using namespace glw; |
| |
| namespace gl4cts |
| { |
| namespace ShaderSubroutine |
| { |
| /** Constructor. |
| * |
| * @param context CTS context. |
| **/ |
| Utils::buffer::buffer(deqp::Context& context) : m_id(0), m_context(context) |
| { |
| } |
| |
| /** Destructor |
| * |
| **/ |
| Utils::buffer::~buffer() |
| { |
| if (0 != m_id) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| gl.deleteBuffers(1, &m_id); |
| m_id = 0; |
| } |
| } |
| |
| /** Execute BindBufferRange |
| * |
| * @param target <target> parameter |
| * @param index <index> parameter |
| * @param offset <offset> parameter |
| * @param size <size> parameter |
| **/ |
| void Utils::buffer::bindRange(glw::GLenum target, glw::GLuint index, glw::GLintptr offset, glw::GLsizeiptr size) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| gl.bindBufferRange(target, index, m_id, offset, size); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindBufferRange"); |
| } |
| |
| /** Execute GenBuffer |
| * |
| **/ |
| void Utils::buffer::generate() |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| gl.genBuffers(1, &m_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GenBuffers"); |
| } |
| |
| /** Execute BufferData |
| * |
| * @param target <target> parameter |
| * @param size <size> parameter |
| * @param data <data> parameter |
| * @param usage <usage> parameter |
| **/ |
| void Utils::buffer::update(glw::GLenum target, glw::GLsizeiptr size, glw::GLvoid* data, glw::GLenum usage) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| gl.bindBuffer(target, m_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "bindBuffer"); |
| |
| gl.bufferData(target, size, data, usage); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "bufferData"); |
| } |
| |
| /** Constructor |
| * |
| * @param context CTS context |
| **/ |
| Utils::framebuffer::framebuffer(deqp::Context& context) : m_id(0), m_context(context) |
| { |
| /* Nothing to be done here */ |
| } |
| |
| /** Destructor |
| * |
| **/ |
| Utils::framebuffer::~framebuffer() |
| { |
| if (0 != m_id) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| gl.deleteFramebuffers(1, &m_id); |
| m_id = 0; |
| } |
| } |
| |
| /** Attach texture to specified attachment |
| * |
| * @param attachment Attachment |
| * @param texture_id Texture id |
| * @param width Texture width |
| * @param height Texture height |
| **/ |
| void Utils::framebuffer::attachTexture(glw::GLenum attachment, glw::GLuint texture_id, glw::GLuint width, |
| glw::GLuint height) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| bind(); |
| |
| gl.bindTexture(GL_TEXTURE_2D, texture_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindTexture"); |
| |
| gl.framebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, GL_TEXTURE_2D, texture_id, 0 /* level */); |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "FramebufferTexture2D"); |
| |
| gl.viewport(0 /* x */, 0 /* y */, width, height); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "Viewport"); |
| } |
| |
| /** Binds framebuffer to DRAW_FRAMEBUFFER |
| * |
| **/ |
| void Utils::framebuffer::bind() |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, m_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindFramebuffer"); |
| } |
| |
| /** Clear framebuffer |
| * |
| * @param mask <mask> parameter of glClear. Decides which shall be cleared |
| **/ |
| void Utils::framebuffer::clear(glw::GLenum mask) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| gl.clear(mask); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "Clear"); |
| } |
| |
| /** Specifie clear color |
| * |
| * @param red Red channel |
| * @param green Green channel |
| * @param blue Blue channel |
| * @param alpha Alpha channel |
| **/ |
| void Utils::framebuffer::clearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| gl.clearColor(red, green, blue, alpha); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "ClearColor"); |
| } |
| |
| /** Generate framebuffer |
| * |
| **/ |
| void Utils::framebuffer::generate() |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| gl.genFramebuffers(1, &m_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GenFramebuffers"); |
| } |
| |
| const glw::GLenum Utils::program::ARB_COMPUTE_SHADER = 0x91B9; |
| |
| /** Constructor. |
| * |
| * @param context CTS context. |
| **/ |
| Utils::program::program(deqp::Context& context) |
| : m_compute_shader_id(0) |
| , m_fragment_shader_id(0) |
| , m_geometry_shader_id(0) |
| , m_program_object_id(0) |
| , m_tesselation_control_shader_id(0) |
| , m_tesselation_evaluation_shader_id(0) |
| , m_vertex_shader_id(0) |
| , m_context(context) |
| { |
| /* Nothing to be done here */ |
| } |
| |
| /** Destructor |
| * |
| **/ |
| Utils::program::~program() |
| { |
| remove(); |
| } |
| |
| /** Build program |
| * |
| * @param compute_shader_code Compute shader source code |
| * @param fragment_shader_code Fragment shader source code |
| * @param geometry_shader_code Geometry shader source code |
| * @param tesselation_control_shader_code Tesselation control shader source code |
| * @param tesselation_evaluation_shader_code Tesselation evaluation shader source code |
| * @param vertex_shader_code Vertex shader source code |
| * @param varying_names Array of strings containing names of varyings to be captured with transfrom feedback |
| * @param n_varying_names Number of varyings to be captured with transfrom feedback |
| * @param is_separable Selects if monolithis or separable program should be built. Defaults to false |
| **/ |
| void Utils::program::build(const glw::GLchar* compute_shader_code, const glw::GLchar* fragment_shader_code, |
| const glw::GLchar* geometry_shader_code, const glw::GLchar* tesselation_control_shader_code, |
| const glw::GLchar* tesselation_evaluation_shader_code, const glw::GLchar* vertex_shader_code, |
| const glw::GLchar* const* varying_names, glw::GLuint n_varying_names, bool is_separable) |
| { |
| /* GL entry points */ |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| /* Create shader objects and compile */ |
| if (0 != compute_shader_code) |
| { |
| m_compute_shader_id = gl.createShader(ARB_COMPUTE_SHADER); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "CreateShader"); |
| |
| compile(m_compute_shader_id, compute_shader_code); |
| } |
| |
| if (0 != fragment_shader_code) |
| { |
| m_fragment_shader_id = gl.createShader(GL_FRAGMENT_SHADER); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "CreateShader"); |
| |
| compile(m_fragment_shader_id, fragment_shader_code); |
| } |
| |
| if (0 != geometry_shader_code) |
| { |
| m_geometry_shader_id = gl.createShader(GL_GEOMETRY_SHADER); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "CreateShader"); |
| |
| compile(m_geometry_shader_id, geometry_shader_code); |
| } |
| |
| if (0 != tesselation_control_shader_code) |
| { |
| m_tesselation_control_shader_id = gl.createShader(GL_TESS_CONTROL_SHADER); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "CreateShader"); |
| |
| compile(m_tesselation_control_shader_id, tesselation_control_shader_code); |
| } |
| |
| if (0 != tesselation_evaluation_shader_code) |
| { |
| m_tesselation_evaluation_shader_id = gl.createShader(GL_TESS_EVALUATION_SHADER); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "CreateShader"); |
| |
| compile(m_tesselation_evaluation_shader_id, tesselation_evaluation_shader_code); |
| } |
| |
| if (0 != vertex_shader_code) |
| { |
| m_vertex_shader_id = gl.createShader(GL_VERTEX_SHADER); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "CreateShader"); |
| |
| compile(m_vertex_shader_id, vertex_shader_code); |
| } |
| |
| /* Create program object */ |
| m_program_object_id = gl.createProgram(); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "CreateProgram"); |
| |
| /* Set up captyured varyings' names */ |
| if (0 != n_varying_names) |
| { |
| gl.transformFeedbackVaryings(m_program_object_id, n_varying_names, varying_names, GL_INTERLEAVED_ATTRIBS); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "TransformFeedbackVaryings"); |
| } |
| |
| /* Set separable parameter */ |
| if (true == is_separable) |
| { |
| gl.programParameteri(m_program_object_id, GL_PROGRAM_SEPARABLE, GL_TRUE); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "ProgramParameteri"); |
| } |
| |
| /* Link program */ |
| link(); |
| } |
| |
| /** Compile shader |
| * |
| * @param shader_id Shader object id |
| * @param shader_code Shader source code |
| **/ |
| void Utils::program::compile(glw::GLuint shader_id, const glw::GLchar* shader_code) const |
| { |
| /* GL entry points */ |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| /* Compilation status */ |
| glw::GLint status = GL_FALSE; |
| |
| /* Set source code */ |
| gl.shaderSource(shader_id, 1 /* count */, &shader_code, 0); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "ShaderSource"); |
| |
| /* Compile */ |
| gl.compileShader(shader_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "CompileShader"); |
| |
| /* Get compilation status */ |
| gl.getShaderiv(shader_id, GL_COMPILE_STATUS, &status); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetShaderiv"); |
| |
| /* Log compilation error */ |
| if (GL_TRUE != status) |
| { |
| glw::GLint length = 0; |
| std::vector<glw::GLchar> message; |
| |
| /* Error log length */ |
| gl.getShaderiv(shader_id, GL_INFO_LOG_LENGTH, &length); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetShaderiv"); |
| |
| /* Prepare storage */ |
| message.resize(length); |
| |
| /* Get error log */ |
| gl.getShaderInfoLog(shader_id, length, 0, &message[0]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetShaderInfoLog"); |
| |
| /* Log */ |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << "Failed to compile shader:\n" |
| << &message[0] << "\nShader source\n" |
| << shader_code << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL("Failed to compile shader"); |
| } |
| } |
| |
| /** Checks whether the tested driver supports GL_ARB_get_program_binary |
| * |
| * @return true if the extension is supported and, also, at least one binary format. |
| **/ |
| bool Utils::program::isProgramBinarySupported() const |
| { |
| glw::GLint n_program_binary_formats = 0; |
| |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| if (m_context.getContextInfo().isExtensionSupported("GL_ARB_get_program_binary")) |
| { |
| gl.getIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &n_program_binary_formats); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() call failed."); |
| } |
| |
| return n_program_binary_formats > 0; |
| } |
| |
| /** Create program from provided binary |
| * |
| * @param binary Buffer with binary form of program |
| * @param binary_format Format of <binary> data |
| **/ |
| void Utils::program::createFromBinary(const std::vector<GLubyte>& binary, GLenum binary_format) |
| { |
| /* GL entry points */ |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| /* Create program object */ |
| m_program_object_id = gl.createProgram(); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "CreateProgram"); |
| |
| gl.programBinary(m_program_object_id, binary_format, &binary[0], (GLsizei)binary.size()); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "ProgramBinary"); |
| } |
| |
| /** Get binary form of program |
| * |
| * @param binary Buffer for binary data |
| * @param binary_format Format of binary data |
| **/ |
| void Utils::program::getBinary(std::vector<GLubyte>& binary, GLenum& binary_format) const |
| { |
| /* GL entry points */ |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| /* Get binary size */ |
| GLint length = 0; |
| gl.getProgramiv(m_program_object_id, GL_PROGRAM_BINARY_LENGTH, &length); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramiv"); |
| |
| /* Allocate storage */ |
| binary.resize(length); |
| |
| /* Get binary */ |
| gl.getProgramBinary(m_program_object_id, (GLsizei)binary.size(), &length, &binary_format, &binary[0]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramBinary"); |
| } |
| |
| /** Get subroutine index |
| * |
| * @param subroutine_name Subroutine name |
| * |
| * @return Index of subroutine |
| **/ |
| GLuint Utils::program::getSubroutineIndex(const glw::GLchar* subroutine_name, glw::GLenum shader_stage) const |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| GLuint index = -1; |
| |
| index = gl.getSubroutineIndex(m_program_object_id, shader_stage, subroutine_name); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetSubroutineIndex"); |
| |
| if (GL_INVALID_INDEX == index) |
| { |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << "Subroutine: " << subroutine_name |
| << " is not available" << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL("Subroutine is not available"); |
| } |
| |
| return index; |
| } |
| |
| /** Get subroutine uniform location |
| * |
| * @param uniform_name Subroutine uniform name |
| * |
| * @return Location of subroutine uniform |
| **/ |
| GLint Utils::program::getSubroutineUniformLocation(const glw::GLchar* uniform_name, glw::GLenum shader_stage) const |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| GLint location = -1; |
| |
| location = gl.getSubroutineUniformLocation(m_program_object_id, shader_stage, uniform_name); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetSubroutineUniformLocation"); |
| |
| if (-1 == location) |
| { |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << "Subroutine uniform: " << uniform_name |
| << " is not available" << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL("Subroutine uniform is not available"); |
| } |
| |
| return location; |
| } |
| |
| /** Get uniform location |
| * |
| * @param uniform_name Subroutine uniform name |
| * |
| * @return Location of uniform |
| **/ |
| GLint Utils::program::getUniformLocation(const glw::GLchar* uniform_name) const |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| GLint location = -1; |
| |
| location = gl.getUniformLocation(m_program_object_id, uniform_name); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetUniformLocation"); |
| |
| if (-1 == location) |
| { |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << "Uniform: " << uniform_name |
| << " is not available" << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL("Uniform is not available"); |
| } |
| |
| return location; |
| } |
| |
| /** Attach shaders and link program |
| * |
| **/ |
| void Utils::program::link() const |
| { |
| /* GL entry points */ |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| /* Link status */ |
| glw::GLint status = GL_FALSE; |
| |
| /* Attach shaders */ |
| if (0 != m_compute_shader_id) |
| { |
| gl.attachShader(m_program_object_id, m_compute_shader_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "AttachShader"); |
| } |
| |
| if (0 != m_fragment_shader_id) |
| { |
| gl.attachShader(m_program_object_id, m_fragment_shader_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "AttachShader"); |
| } |
| |
| if (0 != m_geometry_shader_id) |
| { |
| gl.attachShader(m_program_object_id, m_geometry_shader_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "AttachShader"); |
| } |
| |
| if (0 != m_tesselation_control_shader_id) |
| { |
| gl.attachShader(m_program_object_id, m_tesselation_control_shader_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "AttachShader"); |
| } |
| |
| if (0 != m_tesselation_evaluation_shader_id) |
| { |
| gl.attachShader(m_program_object_id, m_tesselation_evaluation_shader_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "AttachShader"); |
| } |
| |
| if (0 != m_vertex_shader_id) |
| { |
| gl.attachShader(m_program_object_id, m_vertex_shader_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "AttachShader"); |
| } |
| |
| /* Link */ |
| gl.linkProgram(m_program_object_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "LinkProgram"); |
| |
| /* Get link status */ |
| gl.getProgramiv(m_program_object_id, GL_LINK_STATUS, &status); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramiv"); |
| |
| /* Log link error */ |
| if (GL_TRUE != status) |
| { |
| glw::GLint length = 0; |
| std::vector<glw::GLchar> message; |
| |
| /* Get error log length */ |
| gl.getProgramiv(m_program_object_id, GL_INFO_LOG_LENGTH, &length); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramiv"); |
| |
| message.resize(length); |
| |
| /* Get error log */ |
| gl.getProgramInfoLog(m_program_object_id, length, 0, &message[0]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramInfoLog"); |
| |
| /* Log */ |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << "Failed to link program:\n" |
| << &message[0] << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL("Failed to link program"); |
| } |
| } |
| |
| /** Delete program object and all attached shaders |
| * |
| **/ |
| void Utils::program::remove() |
| { |
| /* GL entry points */ |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| /* Make sure program object is no longer used by GL */ |
| gl.useProgram(0); |
| |
| /* Clean program object */ |
| if (0 != m_program_object_id) |
| { |
| gl.deleteProgram(m_program_object_id); |
| m_program_object_id = 0; |
| } |
| |
| /* Clean shaders */ |
| if (0 != m_compute_shader_id) |
| { |
| gl.deleteShader(m_compute_shader_id); |
| m_compute_shader_id = 0; |
| } |
| |
| if (0 != m_fragment_shader_id) |
| { |
| gl.deleteShader(m_fragment_shader_id); |
| m_fragment_shader_id = 0; |
| } |
| |
| if (0 != m_geometry_shader_id) |
| { |
| gl.deleteShader(m_geometry_shader_id); |
| m_geometry_shader_id = 0; |
| } |
| |
| if (0 != m_tesselation_control_shader_id) |
| { |
| gl.deleteShader(m_tesselation_control_shader_id); |
| m_tesselation_control_shader_id = 0; |
| } |
| |
| if (0 != m_tesselation_evaluation_shader_id) |
| { |
| gl.deleteShader(m_tesselation_evaluation_shader_id); |
| m_tesselation_evaluation_shader_id = 0; |
| } |
| |
| if (0 != m_vertex_shader_id) |
| { |
| gl.deleteShader(m_vertex_shader_id); |
| m_vertex_shader_id = 0; |
| } |
| } |
| |
| /** Execute UseProgram |
| * |
| **/ |
| void Utils::program::use() const |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| gl.useProgram(m_program_object_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "UseProgram"); |
| } |
| |
| /** Constructor. |
| * |
| * @param context CTS context. |
| **/ |
| Utils::texture::texture(deqp::Context& context) : m_id(0), m_context(context) |
| { |
| /* Nothing to done here */ |
| } |
| |
| /** Destructor |
| * |
| **/ |
| Utils::texture::~texture() |
| { |
| if (0 != m_id) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| gl.deleteTextures(1, &m_id); |
| m_id = 0; |
| } |
| } |
| |
| /** Bind texture to GL_TEXTURE_2D |
| * |
| **/ |
| void Utils::texture::bind() |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| gl.bindTexture(GL_TEXTURE_2D, m_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindTexture"); |
| } |
| |
| /** Create 2d texture |
| * |
| * @param width Width of texture |
| * @param height Height of texture |
| * @param internal_format Internal format of texture |
| **/ |
| void Utils::texture::create(glw::GLuint width, glw::GLuint height, glw::GLenum internal_format) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| gl.genTextures(1, &m_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GenTextures"); |
| |
| bind(); |
| |
| gl.texStorage2D(GL_TEXTURE_2D, 1 /* levels */, internal_format, width, height); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "TexStorage2D"); |
| } |
| |
| /** Get contents of texture |
| * |
| * @param format Format of image |
| * @param type Type of image |
| * @param out_data Buffer for image |
| **/ |
| void Utils::texture::get(glw::GLenum format, glw::GLenum type, glw::GLvoid* out_data) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| bind(); |
| |
| gl.getTexImage(GL_TEXTURE_2D, 0, format, type, out_data); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetTexImage"); |
| } |
| |
| /** Update contents of texture |
| * |
| * @param width Width of texture |
| * @param height Height of texture |
| * @param format Format of data |
| * @param type Type of data |
| * @param data Buffer with image |
| **/ |
| void Utils::texture::update(glw::GLuint width, glw::GLuint height, glw::GLenum format, glw::GLenum type, |
| glw::GLvoid* data) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| bind(); |
| |
| gl.texSubImage2D(GL_TEXTURE_2D, 0 /* level */, 0 /* x */, 0 /* y */, width, height, format, type, data); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "TexSubImage2D"); |
| } |
| |
| /** Constructor. |
| * |
| * @param context CTS context. |
| **/ |
| Utils::vertexArray::vertexArray(deqp::Context& context) : m_id(0), m_context(context) |
| { |
| } |
| |
| /** Destructor |
| * |
| **/ |
| Utils::vertexArray::~vertexArray() |
| { |
| if (0 != m_id) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| gl.deleteVertexArrays(1, &m_id); |
| |
| m_id = 0; |
| } |
| } |
| |
| /** Execute BindVertexArray |
| * |
| **/ |
| void Utils::vertexArray::bind() |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| gl.bindVertexArray(m_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindVertexArray"); |
| } |
| |
| /** Execute GenVertexArrays |
| * |
| **/ |
| void Utils::vertexArray::generate() |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| gl.genVertexArrays(1, &m_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GenVertexArrays"); |
| } |
| |
| /** Builds a program object consisting of up to 5 shader stages |
| * (vertex/tessellation control/tessellation evaluation/geometry/fragment). |
| * The shaders are attached to the program object, then compiled. Finally, |
| * the program object is linked. |
| * |
| * XFB can be optionally configured for the program object. |
| * |
| * Should an error be reported by GL implementation, a TestError |
| * exception will be thrown. |
| * |
| * @param gl OpenGL functions from the active rendering context. |
| * @param vs_body Body to use for the vertex shader. Can be an empty string. |
| * @param tc_body Body to use for the tessellation control shader. Can be |
| * an empty string. |
| * @param te_body Body to use for the tessellation evaluation shader. Can be |
| * an empty string. |
| * @param gs_body Body to use for the geometry shader. Can be an empty string. |
| * @param fs_body Body to use for the fragment shader. Can be an empty string. |
| * @param xfb_varyings An array of names of varyings to use for XFB. Can be NULL. |
| * @param n_xfb_varyings Amount of XFB varyings defined in @param xfb_varyings.Can be 0. |
| * @param out_vs_id Deref will be used to store GL id of a generated vertex shader. |
| * Can be NULL in which case no vertex shader will be used for the |
| * program object. |
| * @param out_tc_id Deref will be used to store GL id of a generated tess control shader. |
| * Can be NULL in which case no tess control shader will be used for the |
| * program object. |
| * @param out_te_id Deref will be used to store GL id of a generated tess evaluation shader. |
| * Can be NULL in which case no tess evaluation shader will be used for the |
| * program object. |
| * @param out_gs_id Deref will be used to store GL id of a generated geometry shader. |
| * Can be NULL in which case no geometry shader will be used for the |
| * program object. |
| * @param out_fs_id Deref will be used to store GL id of a generated fragment shader. |
| * Can be NULL in which case no fragment shader will be used for the |
| * program object. |
| * @param out_po_id Deref will be used to store GL id of a generated program object. |
| * Must not be NULL. |
| * |
| * @return true if the program was built successfully, false otherwise. |
| * */ |
| bool Utils::buildProgram(const glw::Functions& gl, const std::string& vs_body, const std::string& tc_body, |
| const std::string& te_body, const std::string& gs_body, const std::string& fs_body, |
| const glw::GLchar** xfb_varyings, const unsigned int& n_xfb_varyings, glw::GLuint* out_vs_id, |
| glw::GLuint* out_tc_id, glw::GLuint* out_te_id, glw::GLuint* out_gs_id, glw::GLuint* out_fs_id, |
| glw::GLuint* out_po_id) |
| { |
| bool result = false; |
| |
| /* Link the program object */ |
| glw::GLint link_status = GL_FALSE; |
| |
| /* Create objects, set up shader bodies and attach all requested shaders to the program object */ |
| *out_po_id = gl.createProgram(); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() call failed."); |
| |
| if (out_vs_id != DE_NULL) |
| { |
| const char* vs_body_raw_ptr = vs_body.c_str(); |
| |
| *out_vs_id = gl.createShader(GL_VERTEX_SHADER); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() call failed."); |
| |
| gl.attachShader(*out_po_id, *out_vs_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader() call failed."); |
| |
| gl.shaderSource(*out_vs_id, 1 /* count */, &vs_body_raw_ptr, DE_NULL /* length */); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() call failed."); |
| } |
| |
| if (out_tc_id != DE_NULL) |
| { |
| const char* tc_body_raw_ptr = tc_body.c_str(); |
| |
| *out_tc_id = gl.createShader(GL_TESS_CONTROL_SHADER); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() call failed."); |
| |
| gl.attachShader(*out_po_id, *out_tc_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader() call failed."); |
| |
| gl.shaderSource(*out_tc_id, 1 /* count */, &tc_body_raw_ptr, DE_NULL /* length */); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() call failed."); |
| } |
| |
| if (out_te_id != DE_NULL) |
| { |
| const char* te_body_raw_ptr = te_body.c_str(); |
| |
| *out_te_id = gl.createShader(GL_TESS_EVALUATION_SHADER); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() call failed."); |
| |
| gl.attachShader(*out_po_id, *out_te_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader() call failed."); |
| |
| gl.shaderSource(*out_te_id, 1 /* count */, &te_body_raw_ptr, DE_NULL /* length */); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() call failed."); |
| } |
| |
| if (out_gs_id != DE_NULL) |
| { |
| const char* gs_body_raw_ptr = gs_body.c_str(); |
| |
| *out_gs_id = gl.createShader(GL_GEOMETRY_SHADER); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() call failed."); |
| |
| gl.attachShader(*out_po_id, *out_gs_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader() call failed."); |
| |
| gl.shaderSource(*out_gs_id, 1 /* count */, &gs_body_raw_ptr, DE_NULL /* length */); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() call failed."); |
| } |
| |
| if (out_fs_id != DE_NULL) |
| { |
| const char* fs_body_raw_ptr = fs_body.c_str(); |
| |
| *out_fs_id = gl.createShader(GL_FRAGMENT_SHADER); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() call failed."); |
| |
| gl.attachShader(*out_po_id, *out_fs_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader() call failed."); |
| |
| gl.shaderSource(*out_fs_id, 1 /* count */, &fs_body_raw_ptr, DE_NULL /* length */); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() call failed."); |
| } |
| |
| /* Compile all shaders */ |
| const glw::GLuint so_ids[] = { (out_vs_id != DE_NULL) ? *out_vs_id : 0, (out_tc_id != DE_NULL) ? *out_tc_id : 0, |
| (out_te_id != DE_NULL) ? *out_te_id : 0, (out_gs_id != DE_NULL) ? *out_gs_id : 0, |
| (out_fs_id != DE_NULL) ? *out_fs_id : 0 }; |
| const unsigned int n_so_ids = sizeof(so_ids) / sizeof(so_ids[0]); |
| |
| for (unsigned int n_so_id = 0; n_so_id < n_so_ids; ++n_so_id) |
| { |
| glw::GLuint so_id = so_ids[n_so_id]; |
| |
| if (so_id != 0) |
| { |
| glw::GLint compile_status = GL_FALSE; |
| |
| gl.compileShader(so_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader() call failed."); |
| |
| gl.getShaderiv(so_id, GL_COMPILE_STATUS, &compile_status); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv() call failed."); |
| |
| if (compile_status != GL_TRUE) |
| { |
| goto end; |
| } |
| } /* if (so_id != 0) */ |
| } /* for (all shader objects) */ |
| |
| /* Set up XFB */ |
| if (xfb_varyings != NULL) |
| { |
| gl.transformFeedbackVaryings(*out_po_id, n_xfb_varyings, xfb_varyings, GL_INTERLEAVED_ATTRIBS); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings() call failed."); |
| } |
| |
| gl.linkProgram(*out_po_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glLinkProgram() call failed."); |
| |
| gl.getProgramiv(*out_po_id, GL_LINK_STATUS, &link_status); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv() call failed."); |
| |
| if (link_status != GL_TRUE) |
| { |
| goto end; |
| } |
| |
| /* All done */ |
| result = true; |
| |
| end: |
| return result; |
| } |
| |
| /** Retrieves base variable type for user-specified variable type |
| * (eg. float for vec4) |
| * |
| * @param variable_type Variable type to use for the query. |
| * |
| * @return As per description. |
| **/ |
| Utils::_variable_type Utils::getBaseVariableType(const _variable_type& variable_type) |
| { |
| _variable_type result = VARIABLE_TYPE_UNKNOWN; |
| |
| switch (variable_type) |
| { |
| case VARIABLE_TYPE_BOOL: |
| case VARIABLE_TYPE_BVEC2: |
| case VARIABLE_TYPE_BVEC3: |
| case VARIABLE_TYPE_BVEC4: |
| { |
| result = VARIABLE_TYPE_BOOL; |
| |
| break; |
| } |
| |
| case VARIABLE_TYPE_DOUBLE: |
| case VARIABLE_TYPE_DVEC2: |
| case VARIABLE_TYPE_DVEC3: |
| case VARIABLE_TYPE_DVEC4: |
| { |
| result = VARIABLE_TYPE_DOUBLE; |
| |
| break; |
| } |
| |
| case VARIABLE_TYPE_FLOAT: |
| case VARIABLE_TYPE_MAT2: |
| case VARIABLE_TYPE_MAT2X3: |
| case VARIABLE_TYPE_MAT2X4: |
| case VARIABLE_TYPE_MAT3: |
| case VARIABLE_TYPE_MAT3X2: |
| case VARIABLE_TYPE_MAT3X4: |
| case VARIABLE_TYPE_MAT4: |
| case VARIABLE_TYPE_MAT4X2: |
| case VARIABLE_TYPE_MAT4X3: |
| case VARIABLE_TYPE_VEC2: |
| case VARIABLE_TYPE_VEC3: |
| case VARIABLE_TYPE_VEC4: |
| { |
| result = VARIABLE_TYPE_FLOAT; |
| |
| break; |
| } |
| |
| case VARIABLE_TYPE_INT: |
| case VARIABLE_TYPE_IVEC2: |
| case VARIABLE_TYPE_IVEC3: |
| case VARIABLE_TYPE_IVEC4: |
| { |
| result = VARIABLE_TYPE_INT; |
| |
| break; |
| } |
| |
| case VARIABLE_TYPE_UINT: |
| case VARIABLE_TYPE_UVEC2: |
| case VARIABLE_TYPE_UVEC3: |
| case VARIABLE_TYPE_UVEC4: |
| { |
| result = VARIABLE_TYPE_UINT; |
| |
| break; |
| } |
| |
| default: |
| { |
| TCU_FAIL("Unrecognized variable type"); |
| } |
| } /* switch (variable_type) */ |
| |
| return result; |
| } |
| |
| /** Retrieves size of a single component (in bytes) for user-specified |
| * variable type. |
| * |
| * @param variable_type Variable type to use for the query. |
| * |
| * @return As per description. |
| **/ |
| unsigned int Utils::getComponentSizeForVariableType(const _variable_type& variable_type) |
| { |
| _variable_type base_variable_type = getBaseVariableType(variable_type); |
| unsigned int result = 0; |
| |
| switch (base_variable_type) |
| { |
| case VARIABLE_TYPE_BOOL: |
| result = sizeof(bool); |
| break; |
| case VARIABLE_TYPE_DOUBLE: |
| result = sizeof(double); |
| break; |
| case VARIABLE_TYPE_FLOAT: |
| result = sizeof(float); |
| break; |
| case VARIABLE_TYPE_INT: |
| result = sizeof(int); |
| break; |
| case VARIABLE_TYPE_UINT: |
| result = sizeof(unsigned int); |
| break; |
| |
| default: |
| { |
| TCU_FAIL("Unrecognized base variable type"); |
| } |
| } /* switch (variable_type) */ |
| |
| return result; |
| } |
| |
| /** Retrieves a GLenum value corresponding to internal shader stage |
| * representation. |
| * |
| * @param shader_stage Shader stage to user for the query. |
| * |
| * @return Requested value or GL_NONE if the stage was not recognized. |
| **/ |
| glw::GLenum Utils::getGLenumForShaderStage(const _shader_stage& shader_stage) |
| { |
| glw::GLenum result = GL_NONE; |
| |
| switch (shader_stage) |
| { |
| case SHADER_STAGE_VERTEX: |
| result = GL_VERTEX_SHADER; |
| break; |
| case SHADER_STAGE_TESSELLATION_CONTROL: |
| result = GL_TESS_CONTROL_SHADER; |
| break; |
| case SHADER_STAGE_TESSELLATION_EVALUATION: |
| result = GL_TESS_EVALUATION_SHADER; |
| break; |
| case SHADER_STAGE_GEOMETRY: |
| result = GL_GEOMETRY_SHADER; |
| break; |
| case SHADER_STAGE_FRAGMENT: |
| result = GL_FRAGMENT_SHADER; |
| break; |
| |
| default: |
| { |
| TCU_FAIL("Unrecognized shader stage requested"); |
| } |
| } /* switch (shader_stage) */ |
| |
| return result; |
| } |
| |
| /** Retrieves number of components that user-specified variable type supports. |
| * |
| * @param variable_type GLSL variable type to use for the query. |
| * |
| * @return As per description. |
| **/ |
| unsigned int Utils::getNumberOfComponentsForVariableType(const _variable_type& variable_type) |
| { |
| unsigned int result = 0; |
| |
| switch (variable_type) |
| { |
| case VARIABLE_TYPE_BOOL: |
| case VARIABLE_TYPE_DOUBLE: |
| case VARIABLE_TYPE_FLOAT: |
| case VARIABLE_TYPE_INT: |
| case VARIABLE_TYPE_UINT: |
| { |
| result = 1; |
| |
| break; |
| } |
| |
| case VARIABLE_TYPE_BVEC2: |
| case VARIABLE_TYPE_DVEC2: |
| case VARIABLE_TYPE_IVEC2: |
| case VARIABLE_TYPE_UVEC2: |
| case VARIABLE_TYPE_VEC2: |
| { |
| result = 2; |
| |
| break; |
| } |
| |
| case VARIABLE_TYPE_BVEC3: |
| case VARIABLE_TYPE_DVEC3: |
| case VARIABLE_TYPE_IVEC3: |
| case VARIABLE_TYPE_UVEC3: |
| case VARIABLE_TYPE_VEC3: |
| { |
| result = 3; |
| |
| break; |
| } |
| |
| case VARIABLE_TYPE_BVEC4: |
| case VARIABLE_TYPE_DVEC4: |
| case VARIABLE_TYPE_IVEC4: |
| case VARIABLE_TYPE_MAT2: |
| case VARIABLE_TYPE_UVEC4: |
| case VARIABLE_TYPE_VEC4: |
| { |
| result = 4; |
| |
| break; |
| } |
| |
| case VARIABLE_TYPE_MAT2X3: |
| case VARIABLE_TYPE_MAT3X2: |
| { |
| result = 6; |
| |
| break; |
| } |
| |
| case VARIABLE_TYPE_MAT2X4: |
| case VARIABLE_TYPE_MAT4X2: |
| { |
| result = 8; |
| |
| break; |
| } |
| |
| case VARIABLE_TYPE_MAT3: |
| { |
| result = 9; |
| |
| break; |
| } |
| |
| case VARIABLE_TYPE_MAT3X4: |
| case VARIABLE_TYPE_MAT4X3: |
| { |
| result = 12; |
| |
| break; |
| } |
| |
| case VARIABLE_TYPE_MAT4: |
| { |
| result = 16; |
| |
| break; |
| } |
| |
| default: |
| break; |
| } /* switch (variable_type) */ |
| |
| return result; |
| } |
| |
| /** Retrieves a literal defining user-specified shader stage enum. |
| * |
| * @param shader_stage Shader stage to use for the query. |
| * |
| * @return Requested string or "?" if the stage was not recognized. |
| **/ |
| std::string Utils::getShaderStageString(const _shader_stage& shader_stage) |
| { |
| std::string result = "?"; |
| |
| switch (shader_stage) |
| { |
| case SHADER_STAGE_FRAGMENT: |
| result = "Fragment Shader"; |
| break; |
| case SHADER_STAGE_GEOMETRY: |
| result = "Geometry Shader"; |
| break; |
| case SHADER_STAGE_TESSELLATION_CONTROL: |
| result = "Tessellation Control Shader"; |
| break; |
| case SHADER_STAGE_TESSELLATION_EVALUATION: |
| result = "Tessellation Evaluation Shader"; |
| break; |
| case SHADER_STAGE_VERTEX: |
| result = "Vertex Shader"; |
| break; |
| |
| default: |
| { |
| TCU_FAIL("Unrecognized shader stage"); |
| } |
| } /* switch (shader_stage) */ |
| |
| return result; |
| } |
| |
| /** Retrieves a literal defining user-specified shader stage enum. |
| * |
| * @param shader_stage_glenum Shader stage to use for the query. |
| * |
| * @return Requested string or "?" if the stage was not recognized. |
| **/ |
| std::string Utils::getShaderStageStringFromGLEnum(const glw::GLenum shader_stage_glenum) |
| { |
| std::string result = "?"; |
| |
| switch (shader_stage_glenum) |
| { |
| case GL_FRAGMENT_SHADER: |
| result = "Fragment Shader"; |
| break; |
| case GL_GEOMETRY_SHADER: |
| result = "Geometry Shader"; |
| break; |
| case GL_TESS_CONTROL_SHADER: |
| result = "Tessellation Control Shader"; |
| break; |
| case GL_TESS_EVALUATION_SHADER: |
| result = "Tessellation Evaluation Shader"; |
| break; |
| case GL_VERTEX_SHADER: |
| result = "Vertex Shader"; |
| break; |
| |
| default: |
| { |
| TCU_FAIL("Unrecognized shader string"); |
| } |
| } /* switch (shader_stage_glenum) */ |
| |
| return result; |
| } |
| |
| /** Returns string that represents program interface name |
| * |
| * @param program_interface Program interface |
| * |
| * @return String representation of known program interface |
| **/ |
| const GLchar* Utils::programInterfaceToStr(glw::GLenum program_interface) |
| { |
| const GLchar* string = "Unknown program interface"; |
| |
| switch (program_interface) |
| { |
| case GL_VERTEX_SUBROUTINE: |
| string = "GL_VERTEX_SUBROUTINE"; |
| break; |
| case GL_VERTEX_SUBROUTINE_UNIFORM: |
| string = "GL_VERTEX_SUBROUTINE_UNIFORM"; |
| break; |
| default: |
| TCU_FAIL("Not implemented"); |
| break; |
| }; |
| |
| 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"); |
| break; |
| }; |
| |
| return string; |
| } |
| |
| bool Utils::compare(const glw::GLfloat& left, const glw::GLfloat& right) |
| { |
| static const glw::GLfloat m_epsilon = 0.00001f; |
| |
| if (m_epsilon < std::abs(right - left)) |
| { |
| return false; |
| } |
| else |
| { |
| return true; |
| } |
| } |
| |
| /** Returns a variable type enum corresponding to user-specified base variable type |
| * and the number of components it should support. |
| * |
| * @param base_variable_type Base variable type to use for the query. |
| * @param n_components Number of components to consider for the query. |
| * |
| * @return As per description. |
| **/ |
| Utils::_variable_type Utils::getVariableTypeFromProperties(const _variable_type& base_variable_type, |
| const unsigned int& n_components) |
| { |
| _variable_type result = VARIABLE_TYPE_UNKNOWN; |
| |
| switch (base_variable_type) |
| { |
| case VARIABLE_TYPE_BOOL: |
| { |
| switch (n_components) |
| { |
| case 1: |
| result = VARIABLE_TYPE_BOOL; |
| break; |
| case 2: |
| result = VARIABLE_TYPE_BVEC2; |
| break; |
| case 3: |
| result = VARIABLE_TYPE_BVEC3; |
| break; |
| case 4: |
| result = VARIABLE_TYPE_BVEC4; |
| break; |
| |
| default: |
| { |
| TCU_FAIL("Unsupported number of components requested"); |
| } |
| } /* switch (n_components) */ |
| |
| break; |
| } |
| |
| case VARIABLE_TYPE_DOUBLE: |
| { |
| switch (n_components) |
| { |
| case 1: |
| result = VARIABLE_TYPE_DOUBLE; |
| break; |
| case 2: |
| result = VARIABLE_TYPE_DVEC2; |
| break; |
| case 3: |
| result = VARIABLE_TYPE_DVEC3; |
| break; |
| case 4: |
| result = VARIABLE_TYPE_DVEC4; |
| break; |
| |
| default: |
| { |
| TCU_FAIL("Unsupported number of components requested"); |
| } |
| } /* switch (n_components) */ |
| |
| break; |
| } |
| |
| case VARIABLE_TYPE_FLOAT: |
| { |
| switch (n_components) |
| { |
| case 1: |
| result = VARIABLE_TYPE_FLOAT; |
| break; |
| case 2: |
| result = VARIABLE_TYPE_VEC2; |
| break; |
| case 3: |
| result = VARIABLE_TYPE_VEC3; |
| break; |
| case 4: |
| result = VARIABLE_TYPE_VEC4; |
| break; |
| |
| default: |
| { |
| TCU_FAIL("Unsupported number of components requested"); |
| } |
| } /* switch (n_components) */ |
| |
| break; |
| } |
| |
| case VARIABLE_TYPE_INT: |
| { |
| switch (n_components) |
| { |
| case 1: |
| result = VARIABLE_TYPE_INT; |
| break; |
| case 2: |
| result = VARIABLE_TYPE_IVEC2; |
| break; |
| case 3: |
| result = VARIABLE_TYPE_IVEC3; |
| break; |
| case 4: |
| result = VARIABLE_TYPE_IVEC4; |
| break; |
| |
| default: |
| { |
| TCU_FAIL("Unsupported number of components requested"); |
| } |
| } /* switch (n_components) */ |
| |
| break; |
| } |
| |
| case VARIABLE_TYPE_UINT: |
| { |
| switch (n_components) |
| { |
| case 1: |
| result = VARIABLE_TYPE_UINT; |
| break; |
| case 2: |
| result = VARIABLE_TYPE_UVEC2; |
| break; |
| case 3: |
| result = VARIABLE_TYPE_UVEC3; |
| break; |
| case 4: |
| result = VARIABLE_TYPE_UVEC4; |
| break; |
| |
| default: |
| { |
| TCU_FAIL("Unsupported number of components requested"); |
| } |
| } /* switch (n_components) */ |
| |
| break; |
| } |
| |
| default: |
| { |
| TCU_FAIL("Unrecognized base variable type"); |
| } |
| } /* switch (base_variable_type) */ |
| |
| return result; |
| } |
| |
| /** Returns a GLSL literal corresponding to user-specified variable type. |
| * |
| * @param variable_type Variable type to use for the query. |
| * |
| * @return As per description or [?] if @param variable_type was not |
| * recognized. |
| **/ |
| std::string Utils::getVariableTypeGLSLString(const _variable_type& variable_type) |
| { |
| std::string result = "[?]"; |
| |
| switch (variable_type) |
| { |
| case VARIABLE_TYPE_BOOL: |
| result = "bool"; |
| break; |
| case VARIABLE_TYPE_BVEC2: |
| result = "bvec2"; |
| break; |
| case VARIABLE_TYPE_BVEC3: |
| result = "bvec3"; |
| break; |
| case VARIABLE_TYPE_BVEC4: |
| result = "bvec4"; |
| break; |
| case VARIABLE_TYPE_DOUBLE: |
| result = "double"; |
| break; |
| case VARIABLE_TYPE_DVEC2: |
| result = "dvec2"; |
| break; |
| case VARIABLE_TYPE_DVEC3: |
| result = "dvec3"; |
| break; |
| case VARIABLE_TYPE_DVEC4: |
| result = "dvec4"; |
| break; |
| case VARIABLE_TYPE_FLOAT: |
| result = "float"; |
| break; |
| case VARIABLE_TYPE_INT: |
| result = "int"; |
| break; |
| case VARIABLE_TYPE_IVEC2: |
| result = "ivec2"; |
| break; |
| case VARIABLE_TYPE_IVEC3: |
| result = "ivec3"; |
| break; |
| case VARIABLE_TYPE_IVEC4: |
| result = "ivec4"; |
| break; |
| case VARIABLE_TYPE_MAT2: |
| result = "mat2"; |
| break; |
| case VARIABLE_TYPE_MAT2X3: |
| result = "mat2x3"; |
| break; |
| case VARIABLE_TYPE_MAT2X4: |
| result = "mat2x4"; |
| break; |
| case VARIABLE_TYPE_MAT3: |
| result = "mat3"; |
| break; |
| case VARIABLE_TYPE_MAT3X2: |
| result = "mat3x2"; |
| break; |
| case VARIABLE_TYPE_MAT3X4: |
| result = "mat3x4"; |
| break; |
| case VARIABLE_TYPE_MAT4: |
| result = "mat4"; |
| break; |
| case VARIABLE_TYPE_MAT4X2: |
| result = "mat4x2"; |
| break; |
| case VARIABLE_TYPE_MAT4X3: |
| result = "mat4x3"; |
| break; |
| case VARIABLE_TYPE_UINT: |
| result = "uint"; |
| break; |
| case VARIABLE_TYPE_UVEC2: |
| result = "uvec2"; |
| break; |
| case VARIABLE_TYPE_UVEC3: |
| result = "uvec3"; |
| break; |
| case VARIABLE_TYPE_UVEC4: |
| result = "uvec4"; |
| break; |
| case VARIABLE_TYPE_VEC2: |
| result = "vec2"; |
| break; |
| case VARIABLE_TYPE_VEC3: |
| result = "vec3"; |
| break; |
| case VARIABLE_TYPE_VEC4: |
| result = "vec4"; |
| break; |
| |
| default: |
| { |
| TCU_FAIL("Unrecognized variable type"); |
| } |
| } /* switch (variable_type) */ |
| |
| return result; |
| } |
| |
| /** Constructor. |
| * |
| * @param context Rendering context. |
| * |
| **/ |
| APITest1::APITest1(deqp::Context& context) |
| : TestCase(context, "min_maxes", "Verifies the implementation returns valid GL_MAX_SUBROUTINE* pnames " |
| "which meet the minimum maximum requirements enforced by the spec.") |
| , m_has_test_passed(true) |
| { |
| /* Left blank intentionally */ |
| } |
| |
| /** Executes test iteration. |
| * |
| * @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed. |
| */ |
| tcu::TestNode::IterateResult APITest1::iterate() |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| /* Do not execute the test if GL_ARB_shader_subroutine is not supported */ |
| if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_shader_subroutine")) |
| { |
| throw tcu::NotSupportedError("GL_ARB_shader_subroutine is not supported."); |
| } |
| |
| /* Iterate over all pnames */ |
| const struct |
| { |
| glw::GLenum pname; |
| const char* pname_string; |
| glw::GLint min_value; |
| } pnames[] = { { GL_MAX_SUBROUTINES, "GL_MAX_SUBROUTINES", 256 }, |
| { GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS, "GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS", 1024 } }; |
| const unsigned int n_pnames = sizeof(pnames) / sizeof(pnames[0]); |
| |
| for (unsigned int n_pname = 0; n_pname < n_pnames; ++n_pname) |
| { |
| glw::GLboolean bool_value = GL_FALSE; |
| glw::GLdouble double_value = 0.0; |
| glw::GLfloat float_value = 0.0f; |
| glw::GLint int_value = 0; |
| glw::GLint64 int64_value = 0; |
| const glw::GLint min_value = pnames[n_pname].min_value; |
| const glw::GLenum& pname = pnames[n_pname].pname; |
| const char* pname_string = pnames[n_pname].pname_string; |
| |
| /* Retrieve the pname values */ |
| gl.getBooleanv(pname, &bool_value); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGetBooleanv() call failed."); |
| |
| gl.getDoublev(pname, &double_value); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGetDoublev() call failed."); |
| |
| gl.getFloatv(pname, &float_value); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGetFloatv() call failed."); |
| |
| gl.getIntegerv(pname, &int_value); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() call failed."); |
| |
| gl.getInteger64v(pname, &int64_value); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGetInteger64v() call failed."); |
| |
| /* Make sure the value reported meets the min max requirement */ |
| if (int_value < min_value) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "GL implementation reports a value of [" << int_value |
| << "]" |
| " for property [" |
| << pname_string << "]" |
| ", whereas the min max for the property is [" |
| << min_value << "]." << tcu::TestLog::EndMessage; |
| |
| m_has_test_passed = false; |
| } |
| |
| /* Verify the other getters reported valid values */ |
| const float epsilon = 1e-5f; |
| |
| if (((int_value == 0) && (bool_value == GL_TRUE)) || ((int_value != 0) && (bool_value != GL_TRUE))) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Invalid boolean value [" << bool_value |
| << "]" |
| " reported for property [" |
| << pname_string << "]" |
| " (int value:[" |
| << int_value << "])" << tcu::TestLog::EndMessage; |
| |
| m_has_test_passed = false; |
| } |
| |
| if (de::abs(double_value - (double)int_value) > epsilon) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Invalid double value [" << double_value |
| << "]" |
| " reported for property [" |
| << pname_string << "]" |
| " (int value:[" |
| << int_value << "])" << tcu::TestLog::EndMessage; |
| |
| m_has_test_passed = false; |
| } |
| |
| if (de::abs(float_value - (float)int_value) > epsilon) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Invalid float value [" << float_value |
| << "]" |
| " reported for property [" |
| << pname_string << "]" |
| " (int value:[" |
| << int_value << "])" << tcu::TestLog::EndMessage; |
| |
| m_has_test_passed = false; |
| } |
| |
| if (int64_value != int_value) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Invalid 64-bit integer value [" << float_value |
| << "]" |
| " reported for property [" |
| << pname_string << "]" |
| " (int value:[" |
| << int_value << "])" << tcu::TestLog::EndMessage; |
| |
| m_has_test_passed = false; |
| } |
| } /* for (all pnames) */ |
| |
| if (m_has_test_passed) |
| { |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| } |
| else |
| { |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); |
| } |
| |
| return STOP; |
| } |
| |
| /** Constructor. |
| * |
| * @param context Rendering context. |
| * |
| **/ |
| APITest2::APITest2(deqp::Context& context) |
| : TestCase(context, "name_getters", "Verifies glGetActiveSubroutineName() and glGetActiveSubroutineUniformName() " |
| "functions work correctly.") |
| , m_buffer(DE_NULL) |
| , m_has_test_passed(true) |
| , m_po_id(0) |
| , m_subroutine_name1("subroutine1") |
| , m_subroutine_name2("subroutine2") |
| , m_subroutine_uniform_name("data_provider") |
| , m_vs_id(0) |
| { |
| /* Left blank intentionally */ |
| } |
| |
| /** Destroys all ES objects that may have been created during test initialization, |
| * as well as releases any buffers that may have been allocated during the process. |
| */ |
| void APITest2::deinit() |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| if (m_buffer != DE_NULL) |
| { |
| delete[] m_buffer; |
| |
| m_buffer = DE_NULL; |
| } |
| |
| if (m_po_id != 0) |
| { |
| gl.deleteProgram(m_po_id); |
| |
| m_po_id = 0; |
| } |
| |
| if (m_vs_id != 0) |
| { |
| gl.deleteShader(m_vs_id); |
| |
| m_vs_id = 0; |
| } |
| } |
| |
| /** Returns body of a vertex shader that should be used for the test. |
| * |
| * @return As per description. |
| **/ |
| std::string APITest2::getVertexShaderBody() |
| { |
| return "#version 400\n" |
| "\n" |
| "#extension GL_ARB_shader_subroutine : require\n" |
| "\n" |
| "subroutine int ExampleSubroutineType(int example_argument);\n" |
| "\n" |
| "subroutine(ExampleSubroutineType) int subroutine1(int example_argument)\n" |
| "{\n" |
| " return 1;\n" |
| "}\n" |
| "\n" |
| "subroutine(ExampleSubroutineType) int subroutine2(int example_argument)\n" |
| "{\n" |
| " return 2;\n" |
| "}\n" |
| "\n" |
| "subroutine uniform ExampleSubroutineType data_provider;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = vec4(float(data_provider(0)), vec3(1) );\n" |
| "}\n"; |
| } |
| |
| /** Initializes all ES objects required to run the test. */ |
| void APITest2::initTest() |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| /* Generate program & shader objects */ |
| m_po_id = gl.createProgram(); |
| m_vs_id = gl.createShader(GL_VERTEX_SHADER); |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() or glCreateShader() call(s) failed."); |
| |
| /* Attach the shader to the program object */ |
| gl.attachShader(m_po_id, m_vs_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader() call failed."); |
| |
| /* Compile the shader */ |
| glw::GLint compile_status = GL_FALSE; |
| std::string vs_body = getVertexShaderBody(); |
| const char* vs_body_raw_ptr = vs_body.c_str(); |
| |
| gl.shaderSource(m_vs_id, 1 /* count */, &vs_body_raw_ptr, DE_NULL /* length */); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() call failed."); |
| |
| gl.compileShader(m_vs_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader() call failed."); |
| |
| gl.getShaderiv(m_vs_id, GL_COMPILE_STATUS, &compile_status); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv() call failed."); |
| |
| if (compile_status != GL_TRUE) |
| { |
| TCU_FAIL("Shader compilation failed."); |
| } |
| |
| /* Try to link the program object */ |
| glw::GLint link_status = GL_FALSE; |
| |
| gl.linkProgram(m_po_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glLinkProgram() call failed."); |
| |
| gl.getProgramiv(m_po_id, GL_LINK_STATUS, &link_status); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv() call failed."); |
| |
| if (link_status != GL_TRUE) |
| { |
| TCU_FAIL("Program linking failed."); |
| } |
| |
| /* Perform a few sanity checks */ |
| glw::GLint n_active_subroutines = 0; |
| glw::GLint n_active_subroutine_uniforms = 0; |
| |
| gl.getProgramStageiv(m_po_id, GL_VERTEX_SHADER, GL_ACTIVE_SUBROUTINES, &n_active_subroutines); |
| gl.getProgramStageiv(m_po_id, GL_VERTEX_SHADER, GL_ACTIVE_SUBROUTINE_UNIFORMS, &n_active_subroutine_uniforms); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramStageiv() call failed."); |
| |
| if (n_active_subroutines != 2 /* subroutines declared in vertex shader */) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Invalid amount of active subroutines reported; expected: 2," |
| " reported:" |
| << n_active_subroutines << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL("Invalid GL_ACTIVE_SUBROUTINES property value."); |
| } |
| |
| if (n_active_subroutine_uniforms != 1) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message |
| << "Invalid amount of active subroutine uniforms reported: expected: 1," |
| " reported: " |
| << n_active_subroutine_uniforms << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL("Invalid GL_ACTIVE_SUBROUTINE_UNIFORMS property value."); |
| } |
| } |
| |
| /** Executes test iteration. |
| * |
| * @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed. |
| */ |
| tcu::TestNode::IterateResult APITest2::iterate() |
| { |
| /* Do not execute the test if GL_ARB_shader_subroutine is not supported */ |
| if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_shader_subroutine")) |
| { |
| throw tcu::NotSupportedError("GL_ARB_shader_subroutine is not supported."); |
| } |
| |
| /* Initialize a test program object */ |
| initTest(); |
| |
| /* Verify glGetActiveSubroutineName() works correctly */ |
| verifyGLGetActiveSubroutineNameFunctionality(); |
| |
| /* Verify glGetActiveSubroutineUniformName() works correctly */ |
| verifyGLGetActiveSubroutineUniformNameFunctionality(); |
| |
| /* Done */ |
| if (m_has_test_passed) |
| { |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| } |
| else |
| { |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); |
| } |
| |
| return STOP; |
| } |
| |
| /** Verifies glGetActiveSubroutineName() behaves as per GL_ARB_shader_subroutine |
| * specification. |
| **/ |
| void APITest2::verifyGLGetActiveSubroutineNameFunctionality() |
| { |
| GLsizei expected_length1 = (GLsizei)strlen(m_subroutine_name1) + 1; |
| GLsizei expected_length2 = (GLsizei)strlen(m_subroutine_name1) + 1; |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| GLsizei reported_length = 0; |
| |
| gl.getActiveSubroutineName(m_po_id, GL_VERTEX_SHADER, 0, /* index */ |
| 0, /* bufsize */ |
| DE_NULL, /* length */ |
| DE_NULL); /* name */ |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGetActiveSubroutineName() call failed."); |
| |
| gl.getProgramInterfaceiv(m_po_id, GL_VERTEX_SUBROUTINE, GL_MAX_NAME_LENGTH, &reported_length); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGetActiveSubroutineName() call failed."); |
| |
| if ((reported_length != expected_length1) && (reported_length != expected_length2)) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message |
| << "Invalid active subroutine name length reported:" << reported_length |
| << ", instead of: " << expected_length1 << " or " << expected_length2 |
| << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL("Incorrect length of active subroutine name"); |
| } |
| |
| m_buffer = new glw::GLchar[reported_length]; |
| |
| memset(m_buffer, 0, reported_length); |
| |
| gl.getActiveSubroutineName(m_po_id, GL_VERTEX_SHADER, 0, reported_length, DE_NULL, /* length */ |
| m_buffer); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGetActiveSubroutineName() call failed."); |
| |
| if (strcmp(m_buffer, m_subroutine_name1) != 0 && strcmp(m_buffer, m_subroutine_name2) != 0) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Invalid active subroutine name reported:[" << m_buffer |
| << "]" |
| " instead of:[" |
| << m_subroutine_name1 << "]" |
| " or:[" |
| << m_subroutine_name2 << "]." << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL("Invalid active subroutine name reported."); |
| } |
| |
| delete[] m_buffer; |
| m_buffer = DE_NULL; |
| } |
| |
| /** Verifies glGetActiveSubroutineUniformName() behaves as per GL_ARB_shader_subroutine |
| * specification. |
| **/ |
| void APITest2::verifyGLGetActiveSubroutineUniformNameFunctionality() |
| { |
| GLsizei expected_length = (GLsizei)strlen(m_subroutine_uniform_name); |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| GLsizei reported_length = 0; |
| |
| gl.getActiveSubroutineUniformName(m_po_id, GL_VERTEX_SHADER, 0, /* index */ |
| 0, /* bufsize */ |
| DE_NULL, /* length */ |
| DE_NULL); /* name */ |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGetActiveSubroutineUniformName() call failed."); |
| |
| gl.getActiveSubroutineUniformName(m_po_id, GL_VERTEX_SHADER, 0, /* index */ |
| 0, /* bufsize */ |
| &reported_length, DE_NULL); /* name */ |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGetActiveSubroutineUniformName() call failed."); |
| |
| // reported_length is the actual number of characters written into <name> |
| // If <bufSize> is 0, reported_length should be 0 |
| if (reported_length != 0) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message |
| << "Invalid active subroutine uniform name length reported:" << reported_length |
| << ", instead of: " << 0 << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL("Incorrect length of active subroutine uniform name"); |
| } |
| |
| m_buffer = new glw::GLchar[expected_length + 1]; |
| |
| memset(m_buffer, 0, expected_length + 1); |
| |
| gl.getActiveSubroutineUniformName(m_po_id, GL_VERTEX_SHADER, 0, expected_length + 1, &reported_length, m_buffer); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "getActiveSubroutineUniformName() call failed."); |
| |
| if (reported_length != expected_length) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message |
| << "Invalid active subroutine uniform name length reported:" << reported_length |
| << ", instead of: " << expected_length << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL("Incorrect length of active subroutine uniform name"); |
| } |
| |
| if (strcmp(m_buffer, m_subroutine_uniform_name) != 0) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Invalid active subroutine uniform name reported:[" << m_buffer |
| << "]" |
| " instead of:[" |
| << m_subroutine_uniform_name << "]" << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL("Invalid active subroutine uniform name reported."); |
| } |
| |
| delete[] m_buffer; |
| m_buffer = DE_NULL; |
| } |
| |
| /** Constructor. |
| * |
| * @param context Rendering context. |
| * |
| **/ |
| FunctionalTest1_2::FunctionalTest1_2(deqp::Context& context) |
| : TestCase(context, "two_subroutines_single_subroutine_uniform", |
| "Verifies the subroutines work correctly in a vertex shader for" |
| " bool/float/int/uint/double/*vec*/*mat* argument and return types") |
| , m_has_test_passed(true) |
| , m_po_id(0) |
| , m_po_getter0_subroutine_index(GL_INVALID_INDEX) |
| , m_po_getter1_subroutine_index(GL_INVALID_INDEX) |
| , m_po_subroutine_uniform_index(-1) |
| , m_xfb_bo_id(0) |
| , m_vao_id(0) |
| , m_vs_id(0) |
| { |
| /* Left blank intentionally */ |
| } |
| |
| /** Destroys all ES objects that may have been created during test initialization, |
| * as well as releases any buffers that may have been allocated during the process. |
| */ |
| void FunctionalTest1_2::deinit() |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| deinitTestIteration(); |
| |
| if (m_xfb_bo_id != 0) |
| { |
| gl.deleteBuffers(1, &m_xfb_bo_id); |
| |
| m_xfb_bo_id = 0; |
| } |
| |
| if (m_vao_id != 0) |
| { |
| gl.deleteVertexArrays(1, &m_vao_id); |
| |
| m_vao_id = 0; |
| } |
| } |
| |
| /** Deinitializes GL objects that are iteration-specific */ |
| void FunctionalTest1_2::deinitTestIteration() |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| if (m_po_id != 0) |
| { |
| gl.deleteProgram(m_po_id); |
| |
| m_po_id = 0; |
| } |
| |
| if (m_vs_id != 0) |
| { |
| gl.deleteShader(m_vs_id); |
| |
| m_vs_id = 0; |
| } |
| } |
| |
| /** Executes a single test iteration using user-specified test case propertiesz. |
| * |
| * @param test-case Test case descriptor. |
| * |
| * @return true if the test iteration passed, false otherwise. |
| **/ |
| bool FunctionalTest1_2::executeTestIteration(const _test_case& test_case) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| bool result = true; |
| |
| /* Build the test program */ |
| std::string empty_body; |
| std::string vs_body = getVertexShaderBody(test_case.variable_type, test_case.array_size); |
| const glw::GLchar* xfb_varyings[] = { "result" }; |
| const unsigned int n_xfb_varyings = sizeof(xfb_varyings) / sizeof(xfb_varyings[0]); |
| |
| if (!Utils::buildProgram(gl, vs_body, empty_body, empty_body, empty_body, empty_body, xfb_varyings, n_xfb_varyings, |
| &m_vs_id, NULL, /* out_tc_id */ |
| NULL, /* out_te_id */ |
| NULL, /* out_gs_id */ |
| NULL, &m_po_id)) |
| { |
| TCU_FAIL("Test program failed to build."); |
| } |
| |
| /* Retrieve subroutine locations */ |
| m_po_getter0_subroutine_index = gl.getSubroutineIndex(m_po_id, GL_VERTEX_SHADER, "getter0"); |
| m_po_getter1_subroutine_index = gl.getSubroutineIndex(m_po_id, GL_VERTEX_SHADER, "getter1"); |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGetSubroutineIndex() call(s) failed."); |
| |
| if (m_po_getter0_subroutine_index == GL_INVALID_INDEX || m_po_getter1_subroutine_index == GL_INVALID_INDEX) |
| { |
| TCU_FAIL("At least one subroutine is considered inactive which is invalid."); |
| } |
| |
| /* Retrieve subroutine uniform location */ |
| m_po_subroutine_uniform_index = gl.getSubroutineUniformLocation(m_po_id, GL_VERTEX_SHADER, "colorGetterUniform"); |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGetSubroutineUniformLocation() call failed."); |
| |
| if (m_po_subroutine_uniform_index == -1) |
| { |
| TCU_FAIL("Subroutine uniform is considered inactive which is invalid."); |
| } |
| |
| /* Set up XFB BO storage */ |
| const Utils::_variable_type base_variable_type = Utils::getBaseVariableType(test_case.variable_type); |
| unsigned int iteration_xfb_bo_size = Utils::getComponentSizeForVariableType(base_variable_type) * |
| Utils::getNumberOfComponentsForVariableType(test_case.variable_type); |
| unsigned int total_xfb_bo_size = 0; |
| |
| if (base_variable_type == Utils::VARIABLE_TYPE_BOOL) |
| { |
| /* Boolean varyings are not supported by OpenGL. Instead, we use ints to output |
| * boolean values. */ |
| iteration_xfb_bo_size = static_cast<unsigned int>(iteration_xfb_bo_size * sizeof(int)); |
| } |
| |
| total_xfb_bo_size = iteration_xfb_bo_size * 2 /* subroutines we will be testing */; |
| |
| gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, total_xfb_bo_size, DE_NULL /* data */, GL_STATIC_DRAW); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() call failed."); |
| |
| /* Activate test program object */ |
| gl.useProgram(m_po_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() call failed."); |
| |
| /* Run two iterations. Each iteration should invoke different subroutine. */ |
| const glw::GLuint subroutine_indices[] = { m_po_getter0_subroutine_index, m_po_getter1_subroutine_index }; |
| const unsigned int n_subroutine_indices = sizeof(subroutine_indices) / sizeof(subroutine_indices[0]); |
| |
| for (unsigned int n_subroutine_index = 0; n_subroutine_index < n_subroutine_indices; ++n_subroutine_index) |
| { |
| /* Configure which subroutine should be used for the draw call */ |
| glw::GLuint current_subroutine_index = subroutine_indices[n_subroutine_index]; |
| |
| gl.uniformSubroutinesuiv(GL_VERTEX_SHADER, 1 /* count */, ¤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<GLfloat> inverse_order_negate_data[5] = { |
| Utils::vec4<GLfloat>(-2.0f, -1.0f, 1.0f, 2.0f), Utils::vec4<GLfloat>(2.0f, 1.0f, -1.0f, -2.0f), |
| Utils::vec4<GLfloat>(2.0f, 1.0f, -1.0f, -2.0f), Utils::vec4<GLfloat>(-2.0f, -1.0f, 1.0f, 2.0f), |
| Utils::vec4<GLfloat>(-2.0f, -1.0f, 1.0f, 2.0f), |
| }; |
| |
| static const Utils::vec4<GLfloat> inverse_order_inverse_data[5] = { |
| Utils::vec4<GLfloat>(-2.0f, -1.0f, 1.0f, 2.0f), Utils::vec4<GLfloat>(2.0f, 1.0f, -1.0f, -2.0f), |
| Utils::vec4<GLfloat>(-0.5f, -1.0f, 1.0f, 0.5f), Utils::vec4<GLfloat>(0.5f, 1.0f, -1.0f, -0.5f), |
| Utils::vec4<GLfloat>(0.5f, 1.0f, -1.0f, -0.5f), |
| }; |
| |
| static const Utils::vec4<GLfloat> inverse_order_square_data[5] = { |
| Utils::vec4<GLfloat>(-2.0f, -1.0f, 1.0f, 2.0f), Utils::vec4<GLfloat>(2.0f, 1.0f, -1.0f, -2.0f), |
| Utils::vec4<GLfloat>(4.0f, 1.0f, 1.0f, 4.0f), Utils::vec4<GLfloat>(4.0f, 1.0f, 1.0f, 4.0f), |
| Utils::vec4<GLfloat>(4.0f, 1.0f, 1.0f, 4.0f), |
| }; |
| |
| static const Utils::vec4<GLfloat> negate_inverse_data[5] = { |
| Utils::vec4<GLfloat>(-2.0f, -1.0f, 1.0f, 2.0f), Utils::vec4<GLfloat>(2.0f, 1.0f, -1.0f, -2.0f), |
| Utils::vec4<GLfloat>(-0.5f, -1.0f, 1.0f, 0.5f), Utils::vec4<GLfloat>(0.5f, 1.0f, -1.0f, -0.5f), |
| Utils::vec4<GLfloat>(0.5f, 1.0f, -1.0f, -0.5f), |
| }; |
| |
| static const Utils::vec4<GLfloat> negate_square_data[5] = { |
| Utils::vec4<GLfloat>(-2.0f, -1.0f, 1.0f, 2.0f), Utils::vec4<GLfloat>(2.0f, 1.0f, -1.0f, -2.0f), |
| Utils::vec4<GLfloat>(4.0f, 1.0f, 1.0f, 4.0f), Utils::vec4<GLfloat>(4.0f, 1.0f, 1.0f, 4.0f), |
| Utils::vec4<GLfloat>(-4.0f, -1.0f, -1.0f, -4.0f), |
| }; |
| |
| static const Utils::vec4<GLfloat> inverse_square_data[5] = { |
| Utils::vec4<GLfloat>(-2.0f, -1.0f, 1.0f, 2.0f), Utils::vec4<GLfloat>(-0.5f, -1.0f, 1.0f, 0.5f), |
| Utils::vec4<GLfloat>(4.0f, 1.0f, 1.0f, 4.0f), Utils::vec4<GLfloat>(0.25f, 1.0f, 1.0f, 0.25f), |
| Utils::vec4<GLfloat>(0.25f, 1.0f, 1.0f, 0.25f), |
| }; |
| |
| /* Do not execute the test if GL_ARB_shader_subroutine is not supported */ |
| if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_shader_subroutine")) |
| { |
| throw tcu::NotSupportedError("GL_ARB_shader_subroutine is not supported."); |
| } |
| |
| m_n_active_subroutine_uniforms = 2; |
| m_n_active_subroutine_uniform_locations = 2; |
| m_n_active_subroutines = 4; |
| m_n_active_subroutine_uniform_name_length = 0; |
| m_n_active_subroutine_name_length = 0; |
| m_n_active_subroutine_uniform_size = 1; |
| |
| /* GL objects */ |
| Utils::program program(m_context); |
| Utils::buffer transform_feedback_buffer(m_context); |
| Utils::vertexArray vao(m_context); |
| |
| bool result = true; |
| |
| /* Calculate max name lengths for subroutines and subroutine uniforms */ |
| for (GLint i = 0; i < m_n_active_subroutine_uniforms; ++i) |
| { |
| const GLsizei length = (GLsizei)strlen(subroutine_uniform_names[i]); |
| |
| if (length > m_n_active_subroutine_uniform_name_length) |
| { |
| m_n_active_subroutine_uniform_name_length = length; |
| } |
| } |
| |
| for (GLint i = 0; i < m_n_active_subroutines; ++i) |
| { |
| const GLsizei length = (GLsizei)strlen(subroutine_names[i]); |
| |
| if (length > m_n_active_subroutine_name_length) |
| { |
| m_n_active_subroutine_name_length = length; |
| } |
| } |
| |
| /* Init */ |
| program.build(0 /* cs */, 0 /* fs */, 0 /* gs */, 0 /* tcs */, 0 /* test */, vertex_shader_code, varying_names, |
| n_varyings); |
| |
| vao.generate(); |
| vao.bind(); |
| |
| transform_feedback_buffer.generate(); |
| transform_feedback_buffer.update(GL_TRANSFORM_FEEDBACK_BUFFER, transform_feedback_buffer_size, 0 /* data */, |
| GL_DYNAMIC_COPY); |
| transform_feedback_buffer.bindRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0, transform_feedback_buffer_size); |
| |
| program.use(); |
| |
| /* Inspect Get* API */ |
| if ((false == inspectProgramStageiv(program.m_program_object_id)) || |
| (false == inspectActiveSubroutineUniformiv(program.m_program_object_id, subroutine_uniform_names)) || |
| (false == inspectActiveSubroutineUniformName(program.m_program_object_id, subroutine_uniform_names)) || |
| (false == inspectActiveSubroutineName(program.m_program_object_id, subroutine_names)) || |
| (false == |
| inspectSubroutineBinding(program.m_program_object_id, subroutine_names, subroutine_uniform_names, false))) |
| { |
| result = false; |
| } |
| |
| /* Inspect GetProgram* API */ |
| if (true == m_context.getContextInfo().isExtensionSupported("GL_ARB_program_interface_query")) |
| { |
| if ((false == inspectProgramInterfaceiv(program.m_program_object_id)) || |
| (false == |
| inspectProgramResourceiv(program.m_program_object_id, subroutine_names, subroutine_uniform_names)) || |
| (false == |
| inspectSubroutineBinding(program.m_program_object_id, subroutine_names, subroutine_uniform_names, true))) |
| { |
| result = false; |
| } |
| } |
| |
| /* Test shader execution */ |
| if ((false == testDraw(program.m_program_object_id, subroutine_names[inverse_order_routine_index], |
| subroutine_names[negate_routine_index], subroutine_uniform_names, inverse_order_negate_data, |
| false)) || |
| (false == testDraw(program.m_program_object_id, subroutine_names[inverse_order_routine_index], |
| subroutine_names[inverse_routine_index], subroutine_uniform_names, |
| inverse_order_inverse_data, false)) || |
| (false == testDraw(program.m_program_object_id, subroutine_names[inverse_order_routine_index], |
| subroutine_names[square_routine_index], subroutine_uniform_names, inverse_order_square_data, |
| false)) || |
| (false == testDraw(program.m_program_object_id, subroutine_names[negate_routine_index], |
| subroutine_names[inverse_routine_index], subroutine_uniform_names, negate_inverse_data, |
| false)) || |
| (false == testDraw(program.m_program_object_id, subroutine_names[negate_routine_index], |
| subroutine_names[square_routine_index], subroutine_uniform_names, negate_square_data, |
| false)) || |
| (false == testDraw(program.m_program_object_id, subroutine_names[inverse_routine_index], |
| subroutine_names[square_routine_index], subroutine_uniform_names, inverse_square_data, |
| false))) |
| { |
| result = false; |
| } |
| |
| if (true == m_context.getContextInfo().isExtensionSupported("GL_ARB_program_interface_query")) |
| { |
| if ((false == testDraw(program.m_program_object_id, subroutine_names[inverse_order_routine_index], |
| subroutine_names[negate_routine_index], subroutine_uniform_names, |
| inverse_order_negate_data, true)) || |
| (false == testDraw(program.m_program_object_id, subroutine_names[inverse_order_routine_index], |
| subroutine_names[inverse_routine_index], subroutine_uniform_names, |
| inverse_order_inverse_data, true)) || |
| (false == testDraw(program.m_program_object_id, subroutine_names[inverse_order_routine_index], |
| subroutine_names[square_routine_index], subroutine_uniform_names, |
| inverse_order_square_data, true)) || |
| (false == testDraw(program.m_program_object_id, subroutine_names[negate_routine_index], |
| subroutine_names[inverse_routine_index], subroutine_uniform_names, negate_inverse_data, |
| true)) || |
| (false == testDraw(program.m_program_object_id, subroutine_names[negate_routine_index], |
| subroutine_names[square_routine_index], subroutine_uniform_names, negate_square_data, |
| true)) || |
| (false == testDraw(program.m_program_object_id, subroutine_names[inverse_routine_index], |
| subroutine_names[square_routine_index], subroutine_uniform_names, inverse_square_data, |
| true))) |
| { |
| result = false; |
| } |
| } |
| |
| /* Done */ |
| if (true == result) |
| { |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| } |
| else |
| { |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); |
| } |
| |
| return tcu::TestNode::STOP; |
| } |
| |
| /** Verify result of getProgramStageiv |
| * |
| * @param program_id Program object id |
| * @param pname <pname> parameter for getProgramStageiv |
| * @param expected Expected value |
| * |
| * @return true if result is equal to expected value, flase otherwise |
| **/ |
| bool FunctionalTest3_4::checkProgramStageiv(glw::GLuint program_id, glw::GLenum pname, glw::GLint expected) const |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| GLint value = 0; |
| |
| gl.getProgramStageiv(program_id, GL_VERTEX_SHADER, pname, &value); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramStageiv"); |
| |
| if (expected != value) |
| { |
| m_context.getTestContext().getLog() << tcu::TestLog::Message |
| << "Error. Invalid result. Function: getProgramStageiv. " |
| << "pname: " << Utils::pnameToStr(pname) << ". " |
| << "Result: " << value << ". " |
| << "Expected: " << expected << "." << tcu::TestLog::EndMessage; |
| |
| return false; |
| } |
| else |
| { |
| return true; |
| } |
| } |
| |
| /** Verify result of getProgramResourceiv |
| * |
| * @param program_id Program object id |
| * @param program_interface Program interface |
| * @param pname <pname> parameter for getProgramStageiv |
| * @param resource_name Resource name |
| * @param expected Expected value |
| * |
| * @return true if result is equal to expected value, false otherwise |
| **/ |
| bool FunctionalTest3_4::checkProgramResourceiv(GLuint program_id, GLenum program_interface, GLenum pname, |
| const glw::GLchar* resource_name, GLint expected) const |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| GLuint index = gl.getProgramResourceIndex(program_id, program_interface, resource_name); |
| GLint value = 0; |
| |
| if (GL_INVALID_INDEX == index) |
| { |
| return false; |
| } |
| |
| gl.getProgramResourceiv(program_id, program_interface, index, 1, &pname, 1, 0, &value); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "getProgramResourceiv"); |
| |
| if (expected != value) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "Error. Invalid result. Function: getProgramResourceiv. " |
| << "Program interface: " << Utils::programInterfaceToStr(program_interface) << ". " |
| << "Resource name: " << resource_name << ". " |
| << "Property: " << Utils::pnameToStr(pname) << ". " |
| << "Result: " << value << ". " |
| << "Expected: " << expected << "." << tcu::TestLog::EndMessage; |
| |
| return false; |
| } |
| else |
| { |
| return true; |
| } |
| } |
| |
| /** Verify result of getProgramInterfaceiv |
| * |
| * @param program_id Program object id |
| * @param program_interface Program interface |
| * @param pname <pname> parameter for getProgramStageiv |
| * @param expected Expected value |
| * |
| * @return true if result is equal to expected value, flase otherwise |
| **/ |
| bool FunctionalTest3_4::checkProgramInterfaceiv(GLuint program_id, GLenum program_interface, GLenum pname, |
| GLint expected) const |
| { |
| const glw::Functions& gl = m_context. |