| /*------------------------------------------------------------------------- |
| * OpenGL Conformance Test Suite |
| * ----------------------------- |
| * |
| * Copyright (c) 2015-2016 The Khronos Group Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ /*! |
| * \file |
| * \brief |
| */ /*-------------------------------------------------------------------*/ |
| |
| /** |
| * \file gl4cMultiBindTests.cpp |
| * \brief Implements conformance tests for "Multi Bind" functionality. |
| */ /*-------------------------------------------------------------------*/ |
| |
| #include "gl4cMultiBindTests.hpp" |
| |
| #include "gluDefs.hpp" |
| #include "gluStrUtil.hpp" |
| #include "glwEnums.hpp" |
| #include "glwFunctions.hpp" |
| #include "tcuTestLog.hpp" |
| |
| #include <string> |
| |
| #define DEBUG_ENBALE_MESSAGE_CALLBACK 0 |
| |
| #if DEBUG_ENBALE_MESSAGE_CALLBACK |
| #include <iomanip> |
| #endif /* DEBUG_ENBALE_MESSAGE_CALLBACK */ |
| |
| using namespace glw; |
| |
| namespace gl4cts |
| { |
| namespace MultiBind |
| { |
| |
| #if DEBUG_ENBALE_MESSAGE_CALLBACK |
| /** Debuging procedure. Logs parameters. |
| * |
| * @param source As specified in GL spec. |
| * @param type As specified in GL spec. |
| * @param id As specified in GL spec. |
| * @param severity As specified in GL spec. |
| * @param ignored |
| * @param message As specified in GL spec. |
| * @param info Pointer to instance of deqp::Context used by test. |
| */ |
| void GLW_APIENTRY debug_proc(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei /* length */, |
| const GLchar* message, void* info) |
| { |
| deqp::Context* ctx = (deqp::Context*)info; |
| |
| const GLchar* source_str = "Unknown"; |
| const GLchar* type_str = "Unknown"; |
| const GLchar* severity_str = "Unknown"; |
| |
| switch (source) |
| { |
| case GL_DEBUG_SOURCE_API: |
| source_str = "API"; |
| break; |
| case GL_DEBUG_SOURCE_APPLICATION: |
| source_str = "APP"; |
| break; |
| case GL_DEBUG_SOURCE_OTHER: |
| source_str = "OTR"; |
| break; |
| case GL_DEBUG_SOURCE_SHADER_COMPILER: |
| source_str = "COM"; |
| break; |
| case GL_DEBUG_SOURCE_THIRD_PARTY: |
| source_str = "3RD"; |
| break; |
| case GL_DEBUG_SOURCE_WINDOW_SYSTEM: |
| source_str = "WS"; |
| break; |
| default: |
| break; |
| } |
| |
| switch (type) |
| { |
| case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: |
| type_str = "DEPRECATED_BEHAVIOR"; |
| break; |
| case GL_DEBUG_TYPE_ERROR: |
| type_str = "ERROR"; |
| break; |
| case GL_DEBUG_TYPE_MARKER: |
| type_str = "MARKER"; |
| break; |
| case GL_DEBUG_TYPE_OTHER: |
| type_str = "OTHER"; |
| break; |
| case GL_DEBUG_TYPE_PERFORMANCE: |
| type_str = "PERFORMANCE"; |
| break; |
| case GL_DEBUG_TYPE_POP_GROUP: |
| type_str = "POP_GROUP"; |
| break; |
| case GL_DEBUG_TYPE_PORTABILITY: |
| type_str = "PORTABILITY"; |
| break; |
| case GL_DEBUG_TYPE_PUSH_GROUP: |
| type_str = "PUSH_GROUP"; |
| break; |
| case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: |
| type_str = "UNDEFINED_BEHAVIOR"; |
| break; |
| default: |
| break; |
| } |
| |
| switch (severity) |
| { |
| case GL_DEBUG_SEVERITY_HIGH: |
| severity_str = "H"; |
| break; |
| case GL_DEBUG_SEVERITY_LOW: |
| severity_str = "L"; |
| break; |
| case GL_DEBUG_SEVERITY_MEDIUM: |
| severity_str = "M"; |
| break; |
| case GL_DEBUG_SEVERITY_NOTIFICATION: |
| severity_str = "N"; |
| break; |
| default: |
| break; |
| } |
| |
| ctx->getTestContext().getLog() << tcu::TestLog::Message << "DEBUG_INFO: " << std::setw(3) << source_str << "|" |
| << severity_str << "|" << std::setw(18) << type_str << "|" << std::setw(12) << id |
| << ": " << message << tcu::TestLog::EndMessage; |
| } |
| |
| #endif /* DEBUG_ENBALE_MESSAGE_CALLBACK */ |
| |
| /** Represents buffer instance |
| * Provides basic buffer functionality |
| **/ |
| class Buffer |
| { |
| public: |
| /* Public methods */ |
| /* Ctr & Dtr */ |
| Buffer(); |
| ~Buffer(); |
| |
| /* Init & Release */ |
| void Init(deqp::Context& context); |
| |
| void InitData(deqp::Context& context, glw::GLenum target, glw::GLenum usage, glw::GLsizeiptr size, |
| const glw::GLvoid* data); |
| |
| void Release(); |
| |
| /* Functionality */ |
| void Bind() const; |
| void BindBase(glw::GLuint index) const; |
| |
| /* Public static routines */ |
| /* Functionality */ |
| static void Bind(const glw::Functions& gl, glw::GLuint id, glw::GLenum target); |
| |
| static void BindBase(const glw::Functions& gl, glw::GLuint id, glw::GLenum target, glw::GLuint index); |
| |
| static void Data(const glw::Functions& gl, glw::GLenum target, glw::GLenum usage, glw::GLsizeiptr size, |
| const glw::GLvoid* data); |
| |
| static void Generate(const glw::Functions& gl, glw::GLuint& out_id); |
| |
| static void SubData(const glw::Functions& gl, glw::GLenum target, glw::GLintptr offset, glw::GLsizeiptr size, |
| glw::GLvoid* data); |
| |
| /* Public fields */ |
| glw::GLuint m_id; |
| |
| /* Public constants */ |
| static const glw::GLuint m_invalid_id; |
| |
| private: |
| /* Private enums */ |
| |
| /* Private fields */ |
| deqp::Context* m_context; |
| glw::GLenum m_target; |
| }; |
| |
| /** Represents framebuffer |
| * Provides basic functionality |
| **/ |
| class Framebuffer |
| { |
| public: |
| /* Public methods */ |
| /* Ctr & Dtr */ |
| Framebuffer(deqp::Context& context); |
| ~Framebuffer(); |
| |
| /* Init & Release */ |
| void Release(); |
| |
| /* Public static routines */ |
| static void AttachTexture(const glw::Functions& gl, glw::GLenum target, glw::GLenum attachment, |
| glw::GLuint texture_id, glw::GLint level, glw::GLuint width, glw::GLuint height); |
| |
| static void Bind(const glw::Functions& gl, glw::GLenum target, glw::GLuint id); |
| |
| static void Generate(const glw::Functions& gl, glw::GLuint& out_id); |
| |
| /* Public fields */ |
| glw::GLuint m_id; |
| |
| /* Public constants */ |
| static const glw::GLuint m_invalid_id; |
| |
| private: |
| /* Private fields */ |
| deqp::Context& m_context; |
| }; |
| |
| /** Represents shader instance. |
| * Provides basic functionality for shaders. |
| **/ |
| class Shader |
| { |
| public: |
| /* Public methods */ |
| /* Ctr & Dtr */ |
| Shader(deqp::Context& context); |
| ~Shader(); |
| |
| /* Init & Realese */ |
| void Init(glw::GLenum stage, const std::string& source); |
| void Release(); |
| |
| /* Public static routines */ |
| /* Functionality */ |
| static void Compile(const glw::Functions& gl, glw::GLuint id); |
| |
| static void Create(const glw::Functions& gl, glw::GLenum stage, glw::GLuint& out_id); |
| |
| static void Source(const glw::Functions& gl, glw::GLuint id, const std::string& source); |
| |
| /* Public fields */ |
| glw::GLuint m_id; |
| |
| /* Public constants */ |
| static const glw::GLuint m_invalid_id; |
| |
| private: |
| /* Private fields */ |
| deqp::Context& m_context; |
| }; |
| |
| /** Represents program instance. |
| * Provides basic functionality |
| **/ |
| class Program |
| { |
| public: |
| /* Public methods */ |
| /* Ctr & Dtr */ |
| Program(deqp::Context& context); |
| ~Program(); |
| |
| /* Init & Release */ |
| void Init(const std::string& compute_shader, const std::string& fragment_shader, const std::string& geometry_shader, |
| const std::string& tesselation_control_shader, const std::string& tesselation_evaluation_shader, |
| const std::string& vertex_shader); |
| void Release(); |
| |
| /* Functionality */ |
| void Use() const; |
| |
| /* Public static routines */ |
| /* Functionality */ |
| static void Attach(const glw::Functions& gl, glw::GLuint program_id, glw::GLuint shader_id); |
| |
| static void Create(const glw::Functions& gl, glw::GLuint& out_id); |
| |
| static void Link(const glw::Functions& gl, glw::GLuint id); |
| |
| static void Use(const glw::Functions& gl, glw::GLuint id); |
| |
| /* Public fields */ |
| glw::GLuint m_id; |
| |
| Shader m_compute; |
| Shader m_fragment; |
| Shader m_geometry; |
| Shader m_tess_ctrl; |
| Shader m_tess_eval; |
| Shader m_vertex; |
| |
| /* Public constants */ |
| static const glw::GLuint m_invalid_id; |
| |
| private: |
| /* Private fields */ |
| deqp::Context& m_context; |
| }; |
| |
| /** Represents texture instance |
| **/ |
| class Texture |
| { |
| public: |
| /* Public methods */ |
| /* Ctr & Dtr */ |
| Texture(); |
| ~Texture(); |
| |
| /* Init & Release */ |
| void Init(deqp::Context& context); |
| |
| void InitBuffer(deqp::Context& context, glw::GLenum internal_format, glw::GLuint buffer_id); |
| |
| void InitStorage(deqp::Context& context, glw::GLenum target, glw::GLsizei levels, glw::GLenum internal_format, |
| glw::GLuint width, glw::GLuint height, glw::GLuint depth, bool allow_error = false); |
| |
| void Release(); |
| |
| /* Public static routines */ |
| /* Functionality */ |
| static void Bind(const glw::Functions& gl, glw::GLuint id, glw::GLenum target); |
| |
| static void CompressedImage(const glw::Functions& gl, glw::GLenum target, glw::GLint level, |
| glw::GLenum internal_format, glw::GLuint width, glw::GLuint height, glw::GLuint depth, |
| glw::GLsizei image_size, const glw::GLvoid* data); |
| |
| static void Generate(const glw::Functions& gl, glw::GLuint& out_id); |
| |
| static void GetData(const glw::Functions& gl, glw::GLint level, glw::GLenum target, glw::GLenum format, |
| glw::GLenum type, glw::GLvoid* out_data); |
| |
| static void GetLevelParameter(const glw::Functions& gl, glw::GLenum target, glw::GLint level, glw::GLenum pname, |
| glw::GLint* param); |
| |
| static void Image(const glw::Functions& gl, glw::GLenum target, glw::GLint level, glw::GLenum internal_format, |
| glw::GLuint width, glw::GLuint height, glw::GLuint depth, glw::GLenum format, glw::GLenum type, |
| const glw::GLvoid* data); |
| |
| static void Storage(const glw::Functions& gl, glw::GLenum target, glw::GLsizei levels, glw::GLenum internal_format, |
| glw::GLuint width, glw::GLuint height, glw::GLuint depth, bool allow_error); |
| |
| static void SubImage(const glw::Functions& gl, glw::GLenum target, glw::GLint level, glw::GLint x, glw::GLint y, |
| glw::GLint z, glw::GLsizei width, glw::GLsizei height, glw::GLsizei depth, glw::GLenum format, |
| glw::GLenum type, const glw::GLvoid* pixels); |
| |
| /* Public fields */ |
| glw::GLuint m_id; |
| |
| /* Public constants */ |
| static const glw::GLuint m_invalid_id; |
| |
| private: |
| /* Private fields */ |
| deqp::Context* m_context; |
| }; |
| |
| /* Buffer constants */ |
| const GLuint Buffer::m_invalid_id = -1; |
| |
| /** Constructor. |
| * |
| **/ |
| Buffer::Buffer() : m_id(m_invalid_id), m_context(0), m_target(GL_ARRAY_BUFFER) |
| { |
| } |
| |
| /** Destructor |
| * |
| **/ |
| Buffer::~Buffer() |
| { |
| Release(); |
| |
| m_context = 0; |
| } |
| |
| /** Initialize buffer instance |
| * |
| * @param context CTS context. |
| **/ |
| void Buffer::Init(deqp::Context& context) |
| { |
| Release(); |
| |
| m_context = &context; |
| } |
| |
| /** Initialize buffer instance with some data |
| * |
| * @param context CTS context. |
| * @param target Buffer target |
| * @param usage Buffer usage enum |
| * @param size <size> parameter |
| * @param data <data> parameter |
| **/ |
| void Buffer::InitData(deqp::Context& context, glw::GLenum target, glw::GLenum usage, glw::GLsizeiptr size, |
| const glw::GLvoid* data) |
| { |
| Init(context); |
| |
| m_target = target; |
| |
| const Functions& gl = m_context->getRenderContext().getFunctions(); |
| |
| Generate(gl, m_id); |
| Bind(gl, m_id, m_target); |
| Data(gl, m_target, usage, size, data); |
| } |
| |
| /** Release buffer instance |
| * |
| **/ |
| void Buffer::Release() |
| { |
| if (m_invalid_id != m_id) |
| { |
| const Functions& gl = m_context->getRenderContext().getFunctions(); |
| |
| gl.deleteBuffers(1, &m_id); |
| m_id = m_invalid_id; |
| } |
| } |
| |
| /** Binds buffer to its target |
| * |
| **/ |
| void Buffer::Bind() const |
| { |
| if (m_invalid_id == m_id) |
| { |
| return; |
| } |
| |
| const Functions& gl = m_context->getRenderContext().getFunctions(); |
| |
| Bind(gl, m_id, m_target); |
| } |
| |
| /** Binds indexed buffer |
| * |
| * @param index <index> parameter |
| **/ |
| void Buffer::BindBase(glw::GLuint index) const |
| { |
| if (m_invalid_id == m_id) |
| { |
| return; |
| } |
| |
| const Functions& gl = m_context->getRenderContext().getFunctions(); |
| |
| BindBase(gl, m_id, m_target, index); |
| } |
| |
| /** Bind buffer to given target |
| * |
| * @param gl GL functions |
| * @param id Id of buffer |
| * @param target Buffer target |
| **/ |
| void Buffer::Bind(const glw::Functions& gl, glw::GLuint id, glw::GLenum target) |
| { |
| gl.bindBuffer(target, id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindBuffer"); |
| } |
| |
| /** Binds indexed buffer |
| * |
| * @param gl GL functions |
| * @param id Id of buffer |
| * @param target Buffer target |
| * @param index <index> parameter |
| **/ |
| void Buffer::BindBase(const glw::Functions& gl, glw::GLuint id, glw::GLenum target, glw::GLuint index) |
| { |
| gl.bindBufferBase(target, index, id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindBufferBase"); |
| } |
| |
| /** Allocate memory for buffer and sends initial content |
| * |
| * @param gl GL functions |
| * @param target Buffer target |
| * @param usage Buffer usage enum |
| * @param size <size> parameter |
| * @param data <data> parameter |
| **/ |
| void Buffer::Data(const glw::Functions& gl, glw::GLenum target, glw::GLenum usage, glw::GLsizeiptr size, |
| const glw::GLvoid* data) |
| { |
| gl.bufferData(target, size, data, usage); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BufferData"); |
| } |
| |
| /** Generate buffer |
| * |
| * @param gl GL functions |
| * @param out_id Id of buffer |
| **/ |
| void Buffer::Generate(const glw::Functions& gl, glw::GLuint& out_id) |
| { |
| GLuint id = m_invalid_id; |
| |
| gl.genBuffers(1, &id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GenBuffers"); |
| |
| if (m_invalid_id == id) |
| { |
| TCU_FAIL("Got invalid id"); |
| } |
| |
| out_id = id; |
| } |
| |
| /** Update range of buffer |
| * |
| * @param gl GL functions |
| * @param target Buffer target |
| * @param offset Offset in buffer |
| * @param size <size> parameter |
| * @param data <data> parameter |
| **/ |
| void Buffer::SubData(const glw::Functions& gl, glw::GLenum target, glw::GLintptr offset, glw::GLsizeiptr size, |
| glw::GLvoid* data) |
| { |
| gl.bufferSubData(target, offset, size, data); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BufferSubData"); |
| } |
| |
| /* Framebuffer constants */ |
| const GLuint Framebuffer::m_invalid_id = -1; |
| |
| /** Constructor. |
| * |
| * @param context CTS context. |
| **/ |
| Framebuffer::Framebuffer(deqp::Context& context) : m_id(m_invalid_id), m_context(context) |
| { |
| /* Nothing to done here */ |
| } |
| |
| /** Destructor |
| * |
| **/ |
| Framebuffer::~Framebuffer() |
| { |
| Release(); |
| } |
| |
| /** Release texture instance |
| * |
| **/ |
| void Framebuffer::Release() |
| { |
| if (m_invalid_id != m_id) |
| { |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| gl.deleteFramebuffers(1, &m_id); |
| m_id = m_invalid_id; |
| } |
| } |
| |
| /** Attach texture to specified attachment |
| * |
| * @param gl GL functions |
| * @param target Framebuffer target |
| * @param attachment Attachment |
| * @param texture_id Texture id |
| * @param level Level of mipmap |
| * @param width Texture width |
| * @param height Texture height |
| **/ |
| void Framebuffer::AttachTexture(const glw::Functions& gl, glw::GLenum target, glw::GLenum attachment, |
| glw::GLuint texture_id, glw::GLint level, glw::GLuint width, glw::GLuint height) |
| { |
| gl.framebufferTexture(target, attachment, texture_id, level); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "FramebufferTexture"); |
| |
| gl.viewport(0 /* x */, 0 /* y */, width, height); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "Viewport"); |
| } |
| |
| /** Binds framebuffer to DRAW_FRAMEBUFFER |
| * |
| * @param gl GL functions |
| * @param target Framebuffer target |
| * @param id ID of framebuffer |
| **/ |
| void Framebuffer::Bind(const glw::Functions& gl, glw::GLenum target, glw::GLuint id) |
| { |
| gl.bindFramebuffer(target, id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindFramebuffer"); |
| } |
| |
| /** Generate framebuffer |
| * |
| **/ |
| void Framebuffer::Generate(const glw::Functions& gl, glw::GLuint& out_id) |
| { |
| GLuint id = m_invalid_id; |
| |
| gl.genFramebuffers(1, &id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GenFramebuffers"); |
| |
| if (m_invalid_id == id) |
| { |
| TCU_FAIL("Invalid id"); |
| } |
| |
| out_id = id; |
| } |
| |
| /* Program constants */ |
| const GLuint Program::m_invalid_id = 0; |
| |
| /** Constructor. |
| * |
| * @param context CTS context. |
| **/ |
| Program::Program(deqp::Context& context) |
| : m_id(m_invalid_id) |
| , m_compute(context) |
| , m_fragment(context) |
| , m_geometry(context) |
| , m_tess_ctrl(context) |
| , m_tess_eval(context) |
| , m_vertex(context) |
| , m_context(context) |
| { |
| /* Nothing to be done here */ |
| } |
| |
| /** Destructor |
| * |
| **/ |
| Program::~Program() |
| { |
| Release(); |
| } |
| |
| /** Initialize program instance |
| * |
| * @param compute_shader Compute shader source code |
| * @param fragment_shader Fragment shader source code |
| * @param geometry_shader Geometry shader source code |
| * @param tesselation_control_shader Tesselation control shader source code |
| * @param tesselation_evaluation_shader Tesselation evaluation shader source code |
| * @param vertex_shader Vertex shader source code |
| **/ |
| void Program::Init(const std::string& compute_shader, const std::string& fragment_shader, |
| const std::string& geometry_shader, const std::string& tesselation_control_shader, |
| const std::string& tesselation_evaluation_shader, const std::string& vertex_shader) |
| { |
| /* Delete previous program */ |
| Release(); |
| |
| /* GL entry points */ |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| /* Initialize shaders */ |
| m_compute.Init(GL_COMPUTE_SHADER, compute_shader); |
| m_fragment.Init(GL_FRAGMENT_SHADER, fragment_shader); |
| m_geometry.Init(GL_GEOMETRY_SHADER, geometry_shader); |
| m_tess_ctrl.Init(GL_TESS_CONTROL_SHADER, tesselation_control_shader); |
| m_tess_eval.Init(GL_TESS_EVALUATION_SHADER, tesselation_evaluation_shader); |
| m_vertex.Init(GL_VERTEX_SHADER, vertex_shader); |
| |
| /* Create program, set up transform feedback and attach shaders */ |
| Create(gl, m_id); |
| Attach(gl, m_id, m_compute.m_id); |
| Attach(gl, m_id, m_fragment.m_id); |
| Attach(gl, m_id, m_geometry.m_id); |
| Attach(gl, m_id, m_tess_ctrl.m_id); |
| Attach(gl, m_id, m_tess_eval.m_id); |
| Attach(gl, m_id, m_vertex.m_id); |
| |
| /* Link program */ |
| Link(gl, m_id); |
| } |
| |
| /** Release program instance |
| * |
| **/ |
| void Program::Release() |
| { |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| if (m_invalid_id != m_id) |
| { |
| Use(gl, m_invalid_id); |
| |
| gl.deleteProgram(m_id); |
| m_id = m_invalid_id; |
| } |
| |
| m_compute.Release(); |
| m_fragment.Release(); |
| m_geometry.Release(); |
| m_tess_ctrl.Release(); |
| m_tess_eval.Release(); |
| m_vertex.Release(); |
| } |
| |
| /** Set program as active |
| * |
| **/ |
| void Program::Use() const |
| { |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| Use(gl, m_id); |
| } |
| |
| /** Attach shader to program |
| * |
| * @param gl GL functions |
| * @param program_id Id of program |
| * @param shader_id Id of shader |
| **/ |
| void Program::Attach(const glw::Functions& gl, glw::GLuint program_id, glw::GLuint shader_id) |
| { |
| /* Sanity checks */ |
| if ((m_invalid_id == program_id) || (Shader::m_invalid_id == shader_id)) |
| { |
| return; |
| } |
| |
| gl.attachShader(program_id, shader_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "AttachShader"); |
| } |
| |
| /** Create program instance |
| * |
| * @param gl GL functions |
| * @param out_id Id of program |
| **/ |
| void Program::Create(const glw::Functions& gl, glw::GLuint& out_id) |
| { |
| const GLuint id = gl.createProgram(); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "CreateProgram"); |
| |
| if (m_invalid_id == id) |
| { |
| TCU_FAIL("Failed to create program"); |
| } |
| |
| out_id = id; |
| } |
| |
| /** Link program |
| * |
| * @param gl GL functions |
| * @param id Id of program |
| **/ |
| void Program::Link(const glw::Functions& gl, glw::GLuint id) |
| { |
| GLint status = GL_FALSE; |
| |
| gl.linkProgram(id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "LinkProgram"); |
| |
| /* Get link status */ |
| gl.getProgramiv(id, GL_LINK_STATUS, &status); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramiv"); |
| |
| /* Log link error */ |
| if (GL_TRUE != status) |
| { |
| glw::GLint length = 0; |
| std::string message; |
| |
| /* Get error log length */ |
| gl.getProgramiv(id, GL_INFO_LOG_LENGTH, &length); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramiv"); |
| |
| message.resize(length, 0); |
| |
| /* Get error log */ |
| gl.getProgramInfoLog(id, length, 0, &message[0]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramInfoLog"); |
| |
| TCU_FAIL(message.c_str()); |
| } |
| } |
| |
| /** Use program |
| * |
| * @param gl GL functions |
| * @param id Id of program |
| **/ |
| void Program::Use(const glw::Functions& gl, glw::GLuint id) |
| { |
| gl.useProgram(id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "UseProgram"); |
| } |
| |
| /* Shader's constants */ |
| const GLuint Shader::m_invalid_id = 0; |
| |
| /** Constructor. |
| * |
| * @param context CTS context. |
| **/ |
| Shader::Shader(deqp::Context& context) : m_id(m_invalid_id), m_context(context) |
| { |
| /* Nothing to be done here */ |
| } |
| |
| /** Destructor |
| * |
| **/ |
| Shader::~Shader() |
| { |
| Release(); |
| } |
| |
| /** Initialize shader instance |
| * |
| * @param stage Shader stage |
| * @param source Source code |
| **/ |
| void Shader::Init(glw::GLenum stage, const std::string& source) |
| { |
| if (true == source.empty()) |
| { |
| /* No source == no shader */ |
| return; |
| } |
| |
| /* Delete any previous shader */ |
| Release(); |
| |
| /* Create, set source and compile */ |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| Create(gl, stage, m_id); |
| Source(gl, m_id, source); |
| |
| Compile(gl, m_id); |
| } |
| |
| /** Release shader instance |
| * |
| **/ |
| void Shader::Release() |
| { |
| if (m_invalid_id != m_id) |
| { |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| gl.deleteShader(m_id); |
| m_id = m_invalid_id; |
| } |
| } |
| |
| /** Compile shader |
| * |
| * @param gl GL functions |
| * @param id Shader id |
| **/ |
| void Shader::Compile(const glw::Functions& gl, glw::GLuint id) |
| { |
| GLint status = GL_FALSE; |
| |
| /* Compile */ |
| gl.compileShader(id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "CompileShader"); |
| |
| /* Get compilation status */ |
| gl.getShaderiv(id, GL_COMPILE_STATUS, &status); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetShaderiv"); |
| |
| /* Log compilation error */ |
| if (GL_TRUE != status) |
| { |
| glw::GLint length = 0; |
| std::string message; |
| |
| /* Error log length */ |
| gl.getShaderiv(id, GL_INFO_LOG_LENGTH, &length); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetShaderiv"); |
| |
| /* Prepare storage */ |
| message.resize(length, 0); |
| |
| /* Get error log */ |
| gl.getShaderInfoLog(id, length, 0, &message[0]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetShaderInfoLog"); |
| |
| TCU_FAIL(message.c_str()); |
| } |
| } |
| |
| /** Create shader |
| * |
| * @param gl GL functions |
| * @param stage Shader stage |
| * @param out_id Shader id |
| **/ |
| void Shader::Create(const glw::Functions& gl, glw::GLenum stage, glw::GLuint& out_id) |
| { |
| const GLuint id = gl.createShader(stage); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "CreateShader"); |
| |
| if (m_invalid_id == id) |
| { |
| TCU_FAIL("Failed to create shader"); |
| } |
| |
| out_id = id; |
| } |
| |
| /** Set shader's source code |
| * |
| * @param gl GL functions |
| * @param id Shader id |
| * @param source Shader source code |
| **/ |
| void Shader::Source(const glw::Functions& gl, glw::GLuint id, const std::string& source) |
| { |
| const GLchar* code = source.c_str(); |
| |
| gl.shaderSource(id, 1 /* count */, &code, 0 /* lengths */); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "ShaderSource"); |
| } |
| |
| /* Texture static fields */ |
| const GLuint Texture::m_invalid_id = -1; |
| |
| /** Constructor. |
| * |
| **/ |
| Texture::Texture() : m_id(m_invalid_id), m_context(0) |
| { |
| /* Nothing to done here */ |
| } |
| |
| /** Destructor |
| * |
| **/ |
| Texture::~Texture() |
| { |
| Release(); |
| } |
| |
| /** Initialize texture instance |
| * |
| * @param context Test context |
| **/ |
| void Texture::Init(deqp::Context& context) |
| { |
| Release(); |
| |
| m_context = &context; |
| } |
| |
| /** Initialize texture instance as texture buffer |
| * |
| * @param context Test context |
| * @param internal_format Internal format of texture |
| * @param buufer_id ID of buffer that will be used as storage |
| **/ |
| void Texture::InitBuffer(deqp::Context& context, glw::GLenum internal_format, glw::GLuint buffer_id) |
| { |
| Init(context); |
| |
| const Functions& gl = m_context->getRenderContext().getFunctions(); |
| |
| Generate(gl, m_id); |
| Bind(gl, m_id, GL_TEXTURE_BUFFER); |
| Buffer::Bind(gl, buffer_id, GL_TEXTURE_BUFFER); |
| |
| gl.texBuffer(GL_TEXTURE_BUFFER, internal_format, buffer_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "TexBuffer"); |
| } |
| |
| /** Initialize texture instance with storage |
| * |
| * @param context Test context |
| * @param target Texture target |
| * @param levels Number of levels |
| * @param internal_format Internal format of texture |
| * @param width Width of texture |
| * @param height Height of texture |
| * @param depth Depth of texture |
| **/ |
| void Texture::InitStorage(deqp::Context& context, glw::GLenum target, glw::GLsizei levels, glw::GLenum internal_format, |
| glw::GLuint width, glw::GLuint height, glw::GLuint depth, bool allow_error) |
| { |
| Init(context); |
| |
| const Functions& gl = m_context->getRenderContext().getFunctions(); |
| |
| Generate(gl, m_id); |
| Bind(gl, m_id, target); |
| Storage(gl, target, levels, internal_format, width, height, depth, allow_error); |
| } |
| |
| /** Release texture instance |
| * |
| * @param context CTS context. |
| **/ |
| void Texture::Release() |
| { |
| if (m_invalid_id != m_id) |
| { |
| const Functions& gl = m_context->getRenderContext().getFunctions(); |
| |
| gl.deleteTextures(1, &m_id); |
| m_id = m_invalid_id; |
| } |
| } |
| |
| /** Bind texture to target |
| * |
| * @param gl GL functions |
| * @param id Id of texture |
| * @param tex_type Type of texture |
| **/ |
| void Texture::Bind(const glw::Functions& gl, glw::GLuint id, glw::GLenum target) |
| { |
| gl.bindTexture(target, id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindTexture"); |
| } |
| |
| /** Set contents of compressed texture |
| * |
| * @param gl GL functions |
| * @param target Texture target |
| * @param level Mipmap level |
| * @param internal_format Format of data |
| * @param width Width of texture |
| * @param height Height of texture |
| * @param depth Depth of texture |
| * @param image_size Size of data |
| * @param data Buffer with image data |
| **/ |
| void Texture::CompressedImage(const glw::Functions& gl, glw::GLenum target, glw::GLint level, |
| glw::GLenum internal_format, glw::GLuint width, glw::GLuint height, glw::GLuint depth, |
| glw::GLsizei image_size, const glw::GLvoid* data) |
| { |
| switch (target) |
| { |
| case GL_TEXTURE_1D: |
| gl.compressedTexImage1D(target, level, internal_format, width, 0 /* border */, image_size, data); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "CompressedTexImage1D"); |
| break; |
| case GL_TEXTURE_1D_ARRAY: |
| case GL_TEXTURE_2D: |
| case GL_TEXTURE_RECTANGLE: |
| gl.compressedTexImage2D(target, level, internal_format, width, height, 0 /* border */, image_size, data); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "CompressedTexImage2D"); |
| break; |
| case GL_TEXTURE_CUBE_MAP: |
| gl.compressedTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, level, internal_format, width, height, 0 /* border */, |
| image_size, data); |
| gl.compressedTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, level, internal_format, width, height, 0 /* border */, |
| image_size, data); |
| gl.compressedTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, level, internal_format, width, height, 0 /* border */, |
| image_size, data); |
| gl.compressedTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, level, internal_format, width, height, 0 /* border */, |
| image_size, data); |
| gl.compressedTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, level, internal_format, width, height, 0 /* border */, |
| image_size, data); |
| gl.compressedTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, level, internal_format, width, height, 0 /* border */, |
| image_size, data); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "CompressedTexImage2D"); |
| break; |
| case GL_TEXTURE_3D: |
| case GL_TEXTURE_2D_ARRAY: |
| gl.compressedTexImage3D(target, level, internal_format, width, height, depth, 0 /* border */, image_size, data); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "CompressedTexImage3D"); |
| break; |
| default: |
| TCU_FAIL("Invliad enum"); |
| break; |
| } |
| } |
| |
| /** Generate texture instance |
| * |
| * @param gl GL functions |
| * @param out_id Id of texture |
| **/ |
| void Texture::Generate(const glw::Functions& gl, glw::GLuint& out_id) |
| { |
| GLuint id = m_invalid_id; |
| |
| gl.genTextures(1, &id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GenTextures"); |
| |
| if (m_invalid_id == id) |
| { |
| TCU_FAIL("Invalid id"); |
| } |
| |
| out_id = id; |
| } |
| |
| /** Get texture data |
| * |
| * @param gl GL functions |
| * @param target Texture target |
| * @param format Format of data |
| * @param type Type of data |
| * @param out_data Buffer for data |
| **/ |
| void Texture::GetData(const glw::Functions& gl, glw::GLint level, glw::GLenum target, glw::GLenum format, |
| glw::GLenum type, glw::GLvoid* out_data) |
| { |
| gl.getTexImage(target, level, format, type, out_data); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetTexImage"); |
| } |
| |
| /** Generate texture instance |
| * |
| * @param gl GL functions |
| * @param target Texture target |
| * @param level Mipmap level |
| * @param pname Parameter to query |
| * @param param Result of query |
| **/ |
| void Texture::GetLevelParameter(const glw::Functions& gl, glw::GLenum target, glw::GLint level, glw::GLenum pname, |
| glw::GLint* param) |
| { |
| gl.getTexLevelParameteriv(target, level, pname, param); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetTexLevelParameteriv"); |
| } |
| |
| /** Set contents of texture |
| * |
| * @param gl GL functions |
| * @param target Texture target |
| * @param level Mipmap level |
| * @param internal_format Format of data |
| * @param width Width of texture |
| * @param height Height of texture |
| * @param depth Depth of texture |
| * @param format Format of data |
| * @param type Type of data |
| * @param data Buffer with image data |
| **/ |
| void Texture::Image(const glw::Functions& gl, glw::GLenum target, glw::GLint level, glw::GLenum internal_format, |
| glw::GLuint width, glw::GLuint height, glw::GLuint depth, glw::GLenum format, glw::GLenum type, |
| const glw::GLvoid* data) |
| { |
| switch (target) |
| { |
| case GL_TEXTURE_1D: |
| gl.texImage1D(target, level, internal_format, width, 0 /* border */, format, type, data); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "TexImage1D"); |
| break; |
| case GL_TEXTURE_1D_ARRAY: |
| case GL_TEXTURE_2D: |
| case GL_TEXTURE_RECTANGLE: |
| gl.texImage2D(target, level, internal_format, width, height, 0 /* border */, format, type, data); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "TexImage2D"); |
| break; |
| case GL_TEXTURE_CUBE_MAP: |
| gl.texImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, level, internal_format, width, height, 0 /* border */, format, |
| type, data); |
| gl.texImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, level, internal_format, width, height, 0 /* border */, format, |
| type, data); |
| gl.texImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, level, internal_format, width, height, 0 /* border */, format, |
| type, data); |
| gl.texImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, level, internal_format, width, height, 0 /* border */, format, |
| type, data); |
| gl.texImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, level, internal_format, width, height, 0 /* border */, format, |
| type, data); |
| gl.texImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, level, internal_format, width, height, 0 /* border */, format, |
| type, data); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "TexImage2D"); |
| break; |
| case GL_TEXTURE_3D: |
| case GL_TEXTURE_2D_ARRAY: |
| gl.texImage3D(target, level, internal_format, width, height, depth, 0 /* border */, format, type, data); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "TexImage3D"); |
| break; |
| default: |
| TCU_FAIL("Invliad enum"); |
| break; |
| } |
| } |
| |
| /** Allocate storage for texture |
| * |
| * @param gl GL functions |
| * @param target Texture target |
| * @param levels Number of levels |
| * @param internal_format Internal format of texture |
| * @param width Width of texture |
| * @param height Height of texture |
| * @param depth Depth of texture |
| **/ |
| void Texture::Storage(const glw::Functions& gl, glw::GLenum target, glw::GLsizei levels, glw::GLenum internal_format, |
| glw::GLuint width, glw::GLuint height, glw::GLuint depth, bool allow_error) |
| { |
| switch (target) |
| { |
| case GL_TEXTURE_1D: |
| gl.texStorage1D(target, levels, internal_format, width); |
| if (!allow_error) |
| { |
| GLU_EXPECT_NO_ERROR(gl.getError(), "TexStorage1D"); |
| } |
| break; |
| case GL_TEXTURE_1D_ARRAY: |
| case GL_TEXTURE_2D: |
| case GL_TEXTURE_RECTANGLE: |
| case GL_TEXTURE_CUBE_MAP: |
| gl.texStorage2D(target, levels, internal_format, width, height); |
| if (!allow_error) |
| { |
| GLU_EXPECT_NO_ERROR(gl.getError(), "TexStorage2D"); |
| } |
| break; |
| case GL_TEXTURE_2D_MULTISAMPLE: |
| gl.texStorage2DMultisample(target, levels, internal_format, width, height, GL_FALSE); |
| if (!allow_error) |
| { |
| GLU_EXPECT_NO_ERROR(gl.getError(), "TexStorage2DMultisample"); |
| } |
| break; |
| case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: |
| gl.texStorage3DMultisample(target, levels, internal_format, width, height, depth, GL_FALSE); |
| if (!allow_error) |
| { |
| GLU_EXPECT_NO_ERROR(gl.getError(), "TexStorage3DMultisample"); |
| } |
| break; |
| case GL_TEXTURE_3D: |
| case GL_TEXTURE_2D_ARRAY: |
| case GL_TEXTURE_CUBE_MAP_ARRAY: |
| gl.texStorage3D(target, levels, internal_format, width, height, depth); |
| if (!allow_error) |
| { |
| GLU_EXPECT_NO_ERROR(gl.getError(), "TexStorage3D"); |
| } |
| break; |
| default: |
| TCU_FAIL("Invliad enum"); |
| break; |
| } |
| } |
| |
| /** Set contents of texture |
| * |
| * @param gl GL functions |
| * @param target Texture target |
| * @param level Mipmap level |
| * @param x X offset |
| * @param y Y offset |
| * @param z Z offset |
| * @param width Width of texture |
| * @param height Height of texture |
| * @param depth Depth of texture |
| * @param format Format of data |
| * @param type Type of data |
| * @param pixels Buffer with image data |
| **/ |
| void Texture::SubImage(const glw::Functions& gl, glw::GLenum target, glw::GLint level, glw::GLint x, glw::GLint y, |
| glw::GLint z, glw::GLsizei width, glw::GLsizei height, glw::GLsizei depth, glw::GLenum format, |
| glw::GLenum type, const glw::GLvoid* pixels) |
| { |
| switch (target) |
| { |
| case GL_TEXTURE_1D: |
| gl.texSubImage1D(target, level, x, width, format, type, pixels); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "TexSubImage1D"); |
| break; |
| case GL_TEXTURE_1D_ARRAY: |
| case GL_TEXTURE_2D: |
| case GL_TEXTURE_RECTANGLE: |
| gl.texSubImage2D(target, level, x, y, width, height, format, type, pixels); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "TexSubImage2D"); |
| break; |
| case GL_TEXTURE_CUBE_MAP: |
| gl.texSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, level, x, y, width, height, format, type, pixels); |
| gl.texSubImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, level, x, y, width, height, format, type, pixels); |
| gl.texSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, level, x, y, width, height, format, type, pixels); |
| gl.texSubImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, level, x, y, width, height, format, type, pixels); |
| gl.texSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, level, x, y, width, height, format, type, pixels); |
| gl.texSubImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, level, x, y, width, height, format, type, pixels); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "TexSubImage2D"); |
| break; |
| case GL_TEXTURE_3D: |
| case GL_TEXTURE_2D_ARRAY: |
| case GL_TEXTURE_CUBE_MAP_ARRAY: |
| gl.texSubImage3D(target, level, x, y, z, width, height, depth, format, type, pixels); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "TexSubImage3D"); |
| break; |
| default: |
| TCU_FAIL("Invliad enum"); |
| break; |
| } |
| } |
| |
| /* Gather info about buffer target */ |
| struct bufferTargetInfo |
| { |
| GLenum m_target; |
| GLenum m_pname_alignment; |
| GLenum m_pname_binding; |
| GLenum m_pname_max; |
| GLenum m_pname_max_size; |
| }; |
| |
| /* Gather info about texture target */ |
| struct textureTargetInfo |
| { |
| GLenum m_target; |
| GLenum m_pname_binding; |
| const GLchar* m_name; |
| }; |
| |
| /* Collects information about buffers */ |
| static const bufferTargetInfo s_buffer_infos[] = { |
| { GL_ATOMIC_COUNTER_BUFFER, 0, GL_ATOMIC_COUNTER_BUFFER_BINDING, GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS, |
| GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE }, |
| { |
| GL_TRANSFORM_FEEDBACK_BUFFER, 0, GL_TRANSFORM_FEEDBACK_BUFFER_BINDING, GL_MAX_TRANSFORM_FEEDBACK_BUFFERS, |
| GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS, |
| }, |
| { GL_UNIFORM_BUFFER, GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, GL_UNIFORM_BUFFER_BINDING, GL_MAX_UNIFORM_BUFFER_BINDINGS, |
| GL_MAX_UNIFORM_BLOCK_SIZE }, |
| { GL_SHADER_STORAGE_BUFFER, GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT, GL_SHADER_STORAGE_BUFFER_BINDING, |
| GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, GL_MAX_SHADER_STORAGE_BLOCK_SIZE }, |
| }; |
| |
| static const size_t s_n_buffer_tragets = sizeof(s_buffer_infos) / sizeof(s_buffer_infos[0]); |
| |
| /* Collects information about textures */ |
| static const textureTargetInfo s_texture_infos[] = { |
| { GL_TEXTURE_1D, GL_TEXTURE_BINDING_1D, "1D" }, |
| { GL_TEXTURE_1D_ARRAY, GL_TEXTURE_BINDING_1D_ARRAY, "1D_ARRAY" }, |
| { GL_TEXTURE_2D, GL_TEXTURE_BINDING_2D, "2D" }, |
| { GL_TEXTURE_2D_ARRAY, GL_TEXTURE_BINDING_2D_ARRAY, "2D_ARRAY" }, |
| { GL_TEXTURE_3D, GL_TEXTURE_BINDING_3D, "3D" }, |
| { GL_TEXTURE_BUFFER, GL_TEXTURE_BINDING_BUFFER, "BUFFER" }, |
| { GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BINDING_CUBE_MAP, "CUBE" }, |
| { GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_BINDING_CUBE_MAP_ARRAY, "CUBE_ARRAY" }, |
| { GL_TEXTURE_RECTANGLE, GL_TEXTURE_BINDING_RECTANGLE, "RECTANGLE" }, |
| { GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_BINDING_2D_MULTISAMPLE, "2D_MS" }, |
| { GL_TEXTURE_2D_MULTISAMPLE_ARRAY, GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY, "2D_MS_ARRAY" } |
| }; |
| |
| static const size_t s_n_texture_tragets = sizeof(s_texture_infos) / sizeof(s_texture_infos[0]); |
| |
| /** Macro, verifies generated error, logs error message and throws failure |
| * |
| * @param expected_error Expected error value |
| * @param error_message Message logged if generated error is not the expected one |
| **/ |
| #define CHECK_ERROR(expected_error, error_message) \ |
| { \ |
| GLenum generated_error = gl.getError(); \ |
| \ |
| if (expected_error != generated_error) \ |
| { \ |
| m_context.getTestContext().getLog() \ |
| << tcu::TestLog::Message << "File: " << __FILE__ << ", line: " << __LINE__ \ |
| << ". Got wrong error: " << glu::getErrorStr(generated_error) \ |
| << ", expected: " << glu::getErrorStr(expected_error) << ", message: " << error_message \ |
| << tcu::TestLog::EndMessage; \ |
| TCU_FAIL("Invalid error generated"); \ |
| } \ |
| } |
| |
| /* Prototypes */ |
| void replaceToken(const GLchar* token, size_t& search_position, const GLchar* text, std::string& string); |
| |
| /** Checks binding |
| * |
| * @param context Test contex |
| * @param pname Pname of binding |
| * @param index Index of binding |
| * @param target_name Name of target |
| * @param expected_value Expected value of binding |
| **/ |
| void checkBinding(deqp::Context& context, GLenum pname, GLuint index, const std::string& target_name, |
| GLint expected_value) |
| { |
| const Functions& gl = context.getRenderContext().getFunctions(); |
| |
| GLint binding = -1; |
| |
| gl.getIntegeri_v(pname, index, &binding); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetIntegeri_v"); |
| |
| if (binding != expected_value) |
| { |
| context.getTestContext().getLog() << tcu::TestLog::Message << "Invalid binding: " << binding |
| << ", expected: " << expected_value << ". Target: " << target_name |
| << " at index: " << index << tcu::TestLog::EndMessage; |
| TCU_FAIL("Invalid binding"); |
| } |
| } |
| |
| /** Checks bindings for given texture unit |
| * |
| * @param context Test contex |
| * @param pname Binding pname of <expected_value> |
| * @param index Index of texture unit |
| * @param expected_value Expected value of binding at <pname> target |
| **/ |
| void checkTextureBinding(deqp::Context& context, GLenum pname, GLuint index, GLint expected_value) |
| { |
| const Functions& gl = context.getRenderContext().getFunctions(); |
| |
| for (size_t i = 0; i < s_n_texture_tragets; ++i) |
| { |
| const GLenum pname_binding = s_texture_infos[i].m_pname_binding; |
| const GLchar* target_name = s_texture_infos[i].m_name; |
| |
| GLint binding = -1; |
| GLint value = 0; |
| |
| gl.getIntegeri_v(pname_binding, index, &binding); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetIntegeri_v"); |
| |
| if (pname_binding == pname) |
| { |
| value = (GLint)expected_value; |
| } |
| |
| if (binding != value) |
| { |
| context.getTestContext().getLog() << tcu::TestLog::Message << "Invalid binding: " << binding |
| << ", expected: " << expected_value << ". Target: " << target_name |
| << " at index: " << index << tcu::TestLog::EndMessage; |
| TCU_FAIL("Invalid binding"); |
| } |
| } |
| } |
| |
| /** Checks binding |
| * |
| * @param context Test context |
| * @param index Index of binding |
| * @param expected_value Expected value of binding |
| **/ |
| void checkVertexAttribBinding(deqp::Context& context, GLuint index, GLint expected_value) |
| { |
| const Functions& gl = context.getRenderContext().getFunctions(); |
| |
| GLint binding = -1; |
| |
| gl.getVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &binding); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetVertexAttribiv"); |
| |
| if (binding != expected_value) |
| { |
| context.getTestContext().getLog() << tcu::TestLog::Message << "Invalid binding: " << binding |
| << ", expected: " << expected_value << ". Target: Vertex attribute" |
| << " at index: " << index << tcu::TestLog::EndMessage; |
| TCU_FAIL("Invalid binding"); |
| } |
| } |
| |
| /** Fills MS texture with specified value |
| * |
| * @param context Test context |
| * @param texture_id Index of binding |
| * @param value Value for texture |
| * @param is_array Selects if array target should be used |
| **/ |
| void fillMSTexture(deqp::Context& context, GLuint texture_id, GLuint value, bool is_array) |
| { |
| /* */ |
| static const GLchar* cs = "#version 430 core\n" |
| "\n" |
| "layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n" |
| "\n" |
| "layout (location = 0) writeonly uniform IMAGE uni_image;\n" |
| "\n" |
| "layout (location = 1) uniform uint uni_value;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " const POINT;\n" |
| "\n" |
| " imageStore(uni_image, point, 0, uvec4(uni_value, 0, 0, 0));\n" |
| "}\n" |
| "\n"; |
| |
| static const GLchar* array_image = "uimage2DMSArray"; |
| static const GLchar* array_point = "ivec3 point = ivec3(gl_WorkGroupID.x, gl_WorkGroupID.y, 0)"; |
| static const GLchar* regular_image = "uimage2DMS"; |
| static const GLchar* regular_point = "ivec2 point = ivec2(gl_WorkGroupID.x, gl_WorkGroupID.y)"; |
| |
| /* */ |
| const Functions& gl = context.getRenderContext().getFunctions(); |
| const GLchar* image = (true == is_array) ? array_image : regular_image; |
| const GLchar* point = (true == is_array) ? array_point : regular_point; |
| size_t position = 0; |
| std::string source = cs; |
| |
| /* */ |
| replaceToken("IMAGE", position, image, source); |
| replaceToken("POINT", position, point, source); |
| |
| /* */ |
| Program program(context); |
| program.Init(source.c_str(), "", "", "", "", ""); |
| program.Use(); |
| |
| /* */ |
| if (true == is_array) |
| { |
| gl.bindImageTexture(0 /* unit */, texture_id, 0 /* level */, GL_TRUE /* layered */, 0 /* layer */, |
| GL_WRITE_ONLY, GL_R32UI); |
| } |
| else |
| { |
| gl.bindImageTexture(0 /* unit */, texture_id, 0 /* level */, GL_FALSE /* layered */, 0 /* layer */, |
| GL_WRITE_ONLY, GL_R32UI); |
| } |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindImageTexture"); |
| |
| gl.uniform1i(0 /* location */, 0 /* image unit*/); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform1i"); |
| |
| gl.uniform1ui(1 /* location */, value /* uni_value */); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform1ui"); |
| |
| /* */ |
| gl.dispatchCompute(6, 6, 1); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "DispatchCompute"); |
| } |
| |
| /** Get texture binding pname for given index |
| * |
| * @param index Index of texture target |
| * |
| * @return Pname |
| **/ |
| GLenum getBinding(GLuint index) |
| { |
| if (index < s_n_texture_tragets) |
| { |
| return s_texture_infos[index].m_pname_binding; |
| } |
| else |
| { |
| return GL_TEXTURE_BINDING_2D; |
| } |
| } |
| |
| /** Get texture target for given index |
| * |
| * @param index Index of texture target |
| * |
| * @return Target |
| **/ |
| GLenum getTarget(GLuint index) |
| { |
| if (index < s_n_texture_tragets) |
| { |
| return s_texture_infos[index].m_target; |
| } |
| else |
| { |
| return GL_TEXTURE_2D; |
| } |
| } |
| |
| /** Replace first occurance of <token> with <text> in <string> starting at <search_posistion> |
| * |
| * @param token Token string |
| * @param search_position Position at which find will start, it is updated to position at which replaced text ends |
| * @param text String that will be used as replacement for <token> |
| * @param string String to work on |
| **/ |
| void replaceToken(const GLchar* token, size_t& search_position, const GLchar* text, std::string& string) |
| { |
| const size_t text_length = strlen(text); |
| const size_t token_length = strlen(token); |
| const size_t token_position = string.find(token, search_position); |
| |
| string.replace(token_position, token_length, text, text_length); |
| |
| search_position = token_position + text_length; |
| } |
| |
| /** Constructor |
| * |
| * @param context Test context |
| **/ |
| ErrorsBindBuffersTest::ErrorsBindBuffersTest(deqp::Context& context) |
| : TestCase(context, "errors_bind_buffers", "Verifies that proper errors are generated by buffer binding routines") |
| { |
| /* Nothing to be done */ |
| } |
| |
| /** Execute test |
| * |
| * @return tcu::TestNode::STOP |
| **/ |
| tcu::TestNode::IterateResult ErrorsBindBuffersTest::iterate() |
| { |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| #if DEBUG_ENBALE_MESSAGE_CALLBACK |
| gl.debugMessageCallback(debug_proc, &m_context); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "DebugMessageCallback"); |
| #endif /* DEBUG_ENBALE_MESSAGE_CALLBACK */ |
| |
| /* - INVALID_ENUM when <target> is not valid; */ |
| { |
| static const GLintptr buffer_size = 16; |
| static const GLsizei count = 1; |
| static const GLuint first = 0; |
| static const GLintptr offset = 4; |
| static const GLintptr size = buffer_size - offset; |
| |
| Buffer buffer; |
| |
| buffer.InitData(m_context, GL_ARRAY_BUFFER, GL_DYNAMIC_COPY, buffer_size, 0 /* data */); |
| |
| gl.bindBuffersBase(GL_ARRAY_BUFFER, first, count, &buffer.m_id); |
| CHECK_ERROR(GL_INVALID_ENUM, "BindBuffersBase with invalid <target>"); |
| |
| gl.bindBuffersRange(GL_ARRAY_BUFFER, first, count, &buffer.m_id, &offset, &size); |
| CHECK_ERROR(GL_INVALID_ENUM, "BindBuffersRange with invalid <target>"); |
| } |
| |
| for (size_t i = 0; i < s_n_buffer_tragets; ++i) |
| { |
| static const GLsizei n_buffers = 4; |
| |
| const GLenum pname_alignment = s_buffer_infos[i].m_pname_alignment; |
| const GLenum pname_max = s_buffer_infos[i].m_pname_max; |
| const GLenum target = s_buffer_infos[i].m_target; |
| const std::string& target_name = glu::getBufferTargetStr(target).toString(); |
| |
| GLintptr buffer_size = 16; |
| GLsizei count = n_buffers; |
| GLuint first = 0; |
| GLuint invalid_id = 1; /* Start with 1, as 0 is not valid name */ |
| GLintptr offset = 4; /* ATOMIC and XFB require alignment of 4 */ |
| GLint offset_alignment = 1; |
| GLint max_buffers = 0; |
| GLintptr size = buffer_size - offset; |
| size_t validated_index = n_buffers - 1; |
| |
| /* Get alignment */ |
| if (0 != pname_alignment) |
| { |
| gl.getIntegerv(pname_alignment, &offset_alignment); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetIntegerv"); |
| |
| buffer_size += offset_alignment; |
| offset = offset_alignment; |
| size = buffer_size - offset; |
| } |
| |
| /* Get max */ |
| gl.getIntegerv(pname_max, &max_buffers); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetIntegerv"); |
| |
| /* Select count so <first + count> does not exceed max. |
| * Validated index shall be in the specified range. |
| */ |
| if (n_buffers > max_buffers) |
| { |
| count = max_buffers; |
| validated_index = max_buffers - 1; |
| } |
| |
| /* Storage */ |
| Buffer buffer[n_buffers]; |
| GLuint buffer_ids[n_buffers]; |
| GLintptr offsets[n_buffers]; |
| GLintptr sizes[n_buffers]; |
| |
| /* Prepare buffers */ |
| for (size_t j = 0; j < n_buffers; ++j) |
| { |
| buffer[j].InitData(m_context, target, GL_DYNAMIC_COPY, buffer_size, 0 /* data */); |
| |
| buffer_ids[j] = buffer[j].m_id; |
| offsets[j] = offset; |
| sizes[j] = size; |
| } |
| |
| /* - INVALID_OPERATION when <first> + <count> is greater than allowed limit; */ |
| { |
| GLsizei t_count = n_buffers; |
| GLuint t_first = 0; |
| |
| /* Select first so <first + count> exceeds max, avoid negative first */ |
| if (n_buffers <= max_buffers) |
| { |
| t_first = max_buffers - n_buffers + 1; |
| } |
| else |
| { |
| t_count = max_buffers + 1; |
| /* first = 0; */ |
| } |
| |
| /* Test */ |
| gl.bindBuffersBase(target, t_first, t_count, buffer_ids); |
| CHECK_ERROR(GL_INVALID_OPERATION, |
| "BindBuffersBase with invalid <first> + <count>, target: " << target_name); |
| |
| gl.bindBuffersRange(target, t_first, t_count, buffer_ids, offsets, sizes); |
| CHECK_ERROR(GL_INVALID_OPERATION, |
| "BindBuffersRange with invalid <first> + <count>, target: " << target_name); |
| } |
| |
| /* - INVALID_OPERATION if any value in <buffers> is not zero or the name of |
| * existing buffer; |
| */ |
| { |
| GLuint t_buffer_ids[n_buffers]; |
| |
| memcpy(t_buffer_ids, buffer_ids, sizeof(buffer_ids)); |
| |
| /* Find invalid id */ |
| while (1) |
| { |
| if (GL_TRUE != gl.isBuffer(invalid_id)) |
| { |
| break; |
| } |
| |
| invalid_id += 1; |
| } |
| |
| /* Invalidate the entry */ |
| t_buffer_ids[validated_index] = invalid_id; |
| |
| /* Test */ |
| gl.bindBuffersBase(target, first, count, t_buffer_ids); |
| CHECK_ERROR(GL_INVALID_OPERATION, "BindBuffersBase with invalid buffer id, target: " << target_name); |
| |
| gl.bindBuffersRange(target, first, count, t_buffer_ids, offsets, sizes); |
| CHECK_ERROR(GL_INVALID_OPERATION, "BindBuffersRange with invalid buffer id, target: " << target_name); |
| } |
| |
| /* - INVALID_VALUE if any value in <offsets> is less than zero; */ |
| { |
| GLintptr t_offsets[n_buffers]; |
| GLintptr t_sizes[n_buffers]; |
| |
| memcpy(t_offsets, offsets, sizeof(offsets)); |
| memcpy(t_sizes, sizes, sizeof(sizes)); |
| |
| /* Invalidate the entry */ |
| t_offsets[validated_index] = -1; |
| t_sizes[validated_index] = -1; |
| |
| /* Test */ |
| gl.bindBuffersRange(target, first, count, buffer_ids, t_offsets, sizes); |
| CHECK_ERROR(GL_INVALID_VALUE, "BindBuffersRange with negative offset, target: " << target_name); |
| |
| /* Test */ |
| gl.bindBuffersRange(target, first, count, buffer_ids, offsets, t_sizes); |
| CHECK_ERROR(GL_INVALID_VALUE, "BindBuffersRange with negative size, target: " << target_name); |
| } |
| |
| /* - INVALID_VALUE if any pair of <offsets> and <sizes> exceeds limits. */ |
| { |
| GLintptr t_offsets[n_buffers]; |
| GLintptr t_sizes[n_buffers]; |
| |
| memcpy(t_offsets, offsets, sizeof(offsets)); |
| memcpy(t_sizes, sizes, sizeof(sizes)); |
| |
| /* Invalidate the entry */ |
| t_offsets[validated_index] -= 1; /* Not aligned by required value */ |
| t_sizes[validated_index] = size - 1; /* Not aligned by required value */ |
| |
| /* Test */ |
| gl.bindBuffersRange(target, first, count, buffer_ids, t_offsets, sizes); |
| CHECK_ERROR(GL_INVALID_VALUE, "BindBuffersRange with invalid <offset>, target: " << target_name); |
| |
| /* Test */ |
| if (GL_TRANSFORM_FEEDBACK_BUFFER == target) |
| { |
| gl.bindBuffersRange(target, first, count, buffer_ids, offsets, t_sizes); |
| CHECK_ERROR(GL_INVALID_VALUE, "BindBuffersRange with invalid <size>, target: " << target_name); |
| } |
| } |
| } |
| |
| /* Set result */ |
| m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| |
| /* Done */ |
| return tcu::TestNode::STOP; |
| } |
| |
| /** Constructor |
| * |
| * @param context Test context |
| **/ |
| ErrorsBindTexturesTest::ErrorsBindTexturesTest(deqp::Context& context) |
| : TestCase(context, "errors_bind_textures", "Verifies that proper errors are generated by texture binding routines") |
| { |
| /* Nothing to be done */ |
| } |
| |
| /** Execute test |
| * |
| * @return tcu::TestNode::STOP |
| **/ |
| tcu::TestNode::IterateResult ErrorsBindTexturesTest::iterate() |
| { |
| static const GLuint depth = 8; |
| static const GLuint height = 8; |
| static const GLsizei n_textures = 4; |
| static const GLuint width = 8; |
| |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| #if DEBUG_ENBALE_MESSAGE_CALLBACK |
| gl.debugMessageCallback(debug_proc, &m_context); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "DebugMessageCallback"); |
| #endif /* DEBUG_ENBALE_MESSAGE_CALLBACK */ |
| |
| GLsizei count = n_textures; |
| GLuint first = 0; |
| GLuint invalid_id = 1; /* Start with 1, as 0 is not valid name */ |
| GLint max_textures = 0; |
| size_t validated_index = n_textures - 1; |
| |
| /* Get max */ |
| gl.getIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_textures); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetIntegerv"); |
| |
| /* Select count so <first + count> does not exceed max. |
| * Validated index shall be in the specified range. |
| */ |
| if (n_textures > max_textures) |
| { |
| count = max_textures; |
| validated_index = max_textures - 1; |
| } |
| |
| /* Storage */ |
| Texture texture[n_textures]; |
| GLuint texture_ids[n_textures]; |
| |
| /* Prepare textures */ |
| texture[0].InitStorage(m_context, GL_TEXTURE_2D, 1 /* levels */, GL_RGBA8, width, height, depth); |
| texture[1].InitStorage(m_context, GL_TEXTURE_2D_ARRAY, 1 /* levels */, GL_RGBA8, width, height, depth); |
| texture[2].InitStorage(m_context, GL_TEXTURE_1D_ARRAY, 1 /* levels */, GL_RGBA8, width, height, depth); |
| texture[3].InitStorage(m_context, GL_TEXTURE_3D, 1 /* levels */, GL_RGBA8, width, height, depth); |
| |
| for (size_t i = 0; i < n_textures; ++i) |
| { |
| texture_ids[i] = texture[i].m_id; |
| } |
| |
| /* - INVALID_OPERATION when <first> + <count> exceed limits; */ |
| { |
| GLsizei t_count = n_textures; |
| GLuint t_first = 0; |
| |
| /* Select first so <first + count> exceeds max, avoid negative first */ |
| if (n_textures <= max_textures) |
| { |
| t_first = max_textures - n_textures + 1; |
| } |
| else |
| { |
| t_count = max_textures + 1; |
| /* first = 0; */ |
| } |
| |
| /* Test */ |
| gl.bindTextures(t_first, t_count, texture_ids); |
| CHECK_ERROR(GL_INVALID_OPERATION, "BindTextures with invalid <first> + <count>"); |
| } |
| |
| /* - INVALID_OPERATION if any value in <buffers> is not zero or the name of |
| * existing buffer; |
| */ |
| { |
| GLuint t_texture_ids[n_textures]; |
| |
| memcpy(t_texture_ids, texture_ids, sizeof(texture_ids)); |
| |
| /* Find invalid id */ |
| while (1) |
| { |
| if (GL_TRUE != gl.isTexture(invalid_id)) |
| { |
| break; |
| } |
| |
| invalid_id += 1; |
| } |
| |
| /* Invalidate the entry */ |
| t_texture_ids[validated_index] = invalid_id; |
| |
| /* Test */ |
| gl.bindTextures(first, count, t_texture_ids); |
| CHECK_ERROR(GL_INVALID_OPERATION, "BindTextures with invalid texture id"); |
| } |
| |
| /* Set result */ |
| m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| |
| /* Done */ |
| return tcu::TestNode::STOP; |
| } |
| |
| /** Constructor |
| * |
| * @param context Test context |
| **/ |
| ErrorsBindSamplersTest::ErrorsBindSamplersTest(deqp::Context& context) |
| : TestCase(context, "errors_bind_samplers", "Verifies that proper errors are generated by sampler binding routines") |
| { |
| /* Nothing to be done */ |
| } |
| |
| /** Execute test |
| * |
| * @return tcu::TestNode::STOP |
| **/ |
| tcu::TestNode::IterateResult ErrorsBindSamplersTest::iterate() |
| { |
| static const GLsizei n_samplers = 4; |
| |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| #if DEBUG_ENBALE_MESSAGE_CALLBACK |
| gl.debugMessageCallback(debug_proc, &m_context); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "DebugMessageCallback"); |
| #endif /* DEBUG_ENBALE_MESSAGE_CALLBACK */ |
| |
| GLsizei count = n_samplers; |
| GLuint first = 0; |
| GLuint invalid_id = 1; /* Start with 1, as 0 is not valid name */ |
| GLint max_samplers = 0; |
| size_t validated_index = n_samplers - 1; |
| |
| /* Get max */ |
| gl.getIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_samplers); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetIntegerv"); |
| |
| /* Select count so <first + count> does not exceed max. |
| * Validated index shall be in the specified range. |
| */ |
| if (n_samplers > max_samplers) |
| { |
| count = max_samplers; |
| validated_index = max_samplers - 1; |
| } |
| |
| /* Storage */ |
| GLuint sampler_ids[n_samplers]; |
| |
| /* Prepare samplers */ |
| gl.genSamplers(n_samplers, sampler_ids); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GenSamplers"); |
| |
| try |
| { |
| /* - INVALID_OPERATION when <first> + <count> exceed limits; */ |
| { |
| GLsizei t_count = n_samplers; |
| GLuint t_first = 0; |
| |
| /* Select first so <first + count> exceeds max, avoid negative first */ |
| if (n_samplers <= max_samplers) |
| { |
| t_first = max_samplers - n_samplers + 1; |
| } |
| else |
| { |
| t_count = max_samplers + 1; |
| /* first = 0; */ |
| } |
| |
| /* Test */ |
| gl.bindSamplers(t_first, t_count, sampler_ids); |
| CHECK_ERROR(GL_INVALID_OPERATION, "BindSamplers with invalid <first> + <count>"); |
| } |
| |
| /* - INVALID_OPERATION if any value in <buffers> is not zero or the name of |
| * existing buffer; |
| */ |
| { |
| GLuint t_sampler_ids[n_samplers]; |
| |
| memcpy(t_sampler_ids, sampler_ids, sizeof(sampler_ids)); |
| |
| /* Find invalid id */ |
| while (1) |
| { |
| if (GL_TRUE != gl.isTexture(invalid_id)) |
| { |
| break; |
| } |
| |
| invalid_id += 1; |
| } |
| |
| /* Invalidate the entry */ |
| t_sampler_ids[validated_index] = invalid_id; |
| |
| /* Test */ |
| gl.bindTextures(first, count, t_sampler_ids); |
| CHECK_ERROR(GL_INVALID_OPERATION, "BindSamplers with invalid sampler id"); |
| } |
| } |
| catch (const std::exception&) |
| { |
| gl.deleteSamplers(n_samplers, sampler_ids); |
| |
| TCU_FAIL("Invalid error generated"); |
| } |
| |
| /* Delete samplers */ |
| gl.deleteSamplers(n_samplers, sampler_ids); |
| |
| /* Set result */ |
| m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| |
| /* Done */ |
| return tcu::TestNode::STOP; |
| } |
| |
| /** Constructor |
| * |
| * @param context Test context |
| **/ |
| ErrorsBindImageTexturesTest::ErrorsBindImageTexturesTest(deqp::Context& context) |
| : TestCase(context, "errors_bind_image_textures", |
| "Verifies that proper errors are generated by image binding routines") |
| { |
| /* Nothing to be done */ |
| } |
| |
| /** Execute test |
| * |
| * @return tcu::TestNode::STOP |
| **/ |
| tcu::TestNode::IterateResult ErrorsBindImageTexturesTest::iterate() |
| { |
| static const GLuint depth = 8; |
| static const GLuint height = 8; |
| static const GLsizei n_textures = 4; |
| static const GLuint width = 8; |
| |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| #if DEBUG_ENBALE_MESSAGE_CALLBACK |
| gl.debugMessageCallback(debug_proc, &m_context); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "DebugMessageCallback"); |
| #endif /* DEBUG_ENBALE_MESSAGE_CALLBACK */ |
| |
| GLsizei count = n_textures; |
| GLuint first = 0; |
| GLuint invalid_id = 1; /* Start with 1, as 0 is not valid name */ |
| GLint max_textures = 0; |
| size_t validated_index = n_textures - 1; |
| |
| /* Get max */ |
| gl.getIntegerv(GL_MAX_IMAGE_UNITS, &max_textures); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetIntegerv"); |
| |
| /* Select count so <first + count> does not exceed max. |
| * Validated index shall be in the specified range. |
| */ |
| if (n_textures > max_textures) |
| { |
| count = max_textures; |
| validated_index = max_textures - 1; |
| } |
| |
| /* Storage */ |
| Texture texture[n_textures]; |
| GLuint texture_ids[n_textures]; |
| |
| /* Prepare textures */ |
| texture[0].InitStorage(m_context, GL_TEXTURE_2D, 1, GL_RGBA8, width, height, depth); |
| texture[1].InitStorage(m_context, GL_TEXTURE_2D_ARRAY, 1, GL_RGBA8, width, height, depth); |
| texture[2].InitStorage(m_context, GL_TEXTURE_1D_ARRAY, 1, GL_RGBA8, width, height, depth); |
| texture[3].InitStorage(m_context, GL_TEXTURE_3D, 1, GL_RGBA8, width, height, depth); |
| |
| for (size_t i = 0; i < n_textures; ++i) |
| { |
| texture_ids[i] = texture[i].m_id; |
| } |
| |
| /* - INVALID_OPERATION when <first> + <count> exceed limits; */ |
| { |
| GLsizei t_count = n_textures; |
| GLuint t_first = 0; |
| |
| /* Select first so <first + count> exceeds max, avoid negative first */ |
| if (n_textures <= max_textures) |
| { |
| t_first = max_textures - n_textures + 1; |
| } |
| else |
| { |
| t_count = max_textures + 1; |
| /* first = 0; */ |
| } |
| |
| /* Test */ |
| gl.bindImageTextures(t_first, t_count, texture_ids); |
| CHECK_ERROR(GL_INVALID_OPERATION, "BindImageTextures with invalid <first> + <count>"); |
| } |
| |
| /* - INVALID_OPERATION if any value in <buffers> is not zero or the name of |
| * existing buffer; |
| */ |
| { |
| GLuint t_texture_ids[n_textures]; |
| |
| memcpy(t_texture_ids, texture_ids, sizeof(texture_ids)); |
| |
| /* Find invalid id */ |
| while (1) |
| { |
| if (GL_TRUE != gl.isTexture(invalid_id)) |
| { |
| break; |
| } |
| |
| invalid_id += 1; |
| } |
| |
| /* Invalidate the entry */ |
| t_texture_ids[validated_index] = invalid_id; |
| |
| /* Test */ |
| gl.bindImageTextures(first, count, t_texture_ids); |
| CHECK_ERROR(GL_INVALID_OPERATION, "BindImageTextures with invalid texture id"); |
| } |
| |
| /* - INVALID_OPERATION if any entry found in <textures> has invalid internal |
| * format at level 0; |
| */ |
| { |
| GLuint t_texture_ids[n_textures]; |
| |
| memcpy(t_texture_ids, texture_ids, sizeof(texture_ids)); |
| |
| /* Prepare texture with invalid format */ |
| Texture t_texture; |
| t_texture.Init(m_context); |
| t_texture.Generate(gl, t_texture.m_id); |
| t_texture.Bind(gl, t_texture.m_id, GL_TEXTURE_2D); |
| gl.texStorage2D(GL_TEXTURE_2D, 1, GL_RGB9_E5, width, 0); |
| CHECK_ERROR(GL_INVALID_VALUE, "texStorage2D has height set to 0"); |
| |
| /* Invalidate the entry */ |
| t_texture_ids[validated_index] = t_texture.m_id; |
| |
| /* Test */ |
| gl.bindImageTextures(first, count, t_texture_ids); |
| CHECK_ERROR(GL_INVALID_OPERATION, "BindImageTextures with invalid internal format"); |
| } |
| |
| /* - INVALID_VALUE when any entry in <textures> has any of dimensions equal |
| * to 0 at level 0. |
| */ |
| { |
| GLuint t_texture_ids[n_textures]; |
| |
| memcpy(t_texture_ids, texture_ids, sizeof(texture_ids)); |
| |
| /* Prepare texture with invalid format */ |
| Texture t_texture; |
| t_texture.InitStorage(m_context, GL_TEXTURE_2D, 1, GL_RGB9_E5, width, 0, depth, true); |
| |
| /* Invalidate the entry */ |
| t_texture_ids[validated_index] = t_texture.m_id; |
| |
| /* Test */ |
| gl.bindImageTextures(first, count, t_texture_ids); |
| CHECK_ERROR(GL_INVALID_VALUE, "BindImageTextures with 2D texture that has height set to 0"); |
| } |
| |
| /* Set result */ |
| m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| |
| /* Done */ |
| return tcu::TestNode::STOP; |
| } |
| |
| /** Constructor |
| * |
| * @param context Test context |
| **/ |
| ErrorsBindVertexBuffersTest::ErrorsBindVertexBuffersTest(deqp::Context& context) |
| : TestCase(context, "errors_bind_vertex_buffers", |
| "Verifies that proper errors are generated by vertex buffer binding routines") |
| { |
| /* Nothing to be done */ |
| } |
| |
| /** Execute test |
| * |
| * @return tcu::TestNode::STOP |
| **/ |
| tcu::TestNode::IterateResult ErrorsBindVertexBuffersTest::iterate() |
| { |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| #if DEBUG_ENBALE_MESSAGE_CALLBACK |
| gl.debugMessageCallback(debug_proc, &m_context); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "DebugMessageCallback"); |
| #endif /* DEBUG_ENBALE_MESSAGE_CALLBACK */ |
| |
| static const GLsizei n_buffers = 4; |
| static const GLsizei stride = 4; |
| |
| GLintptr buffer_size = 16; |
| GLsizei count = n_buffers; |
| GLuint first = 0; |
| GLuint invalid_id = 1; /* Start with 1, as 0 is not valid name */ |
| GLintptr offset = 4; /* ATOMIC and XFB require alignment of 4 */ |
| GLint max_buffers = 0; |
| size_t validated_index = n_buffers - 1; |
| |
| /* Get max */ |
| gl.getIntegerv(GL_MAX_VERTEX_ATTRIB_BINDINGS, &max_buffers); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetIntegerv"); |
| |
| /* Select count so <first + count> does not exceed max. |
| * Validated index shall be in the specified range. |
| */ |
| if (n_buffers > max_buffers) |
| { |
| count = max_buffers; |
| validated_index = max_buffers - 1; |
| } |
| |
| /* Storage */ |
| Buffer buffer[n_buffers]; |
| GLuint buffer_ids[n_buffers]; |
| GLintptr offsets[n_buffers]; |
| GLsizei strides[n_buffers]; |
| |
| /* Prepare buffers */ |
| for (size_t j = 0; j < n_buffers; ++j) |
| { |
| buffer[j].InitData(m_context, GL_ARRAY_BUFFER, GL_DYNAMIC_COPY, buffer_size, 0 /* data */); |
| |
| buffer_ids[j] = buffer[j].m_id; |
| offsets[j] = offset; |
| strides[j] = stride; |
| } |
| |
| /* Prepare VAO */ |
| GLuint vao = 0; |
| gl.genVertexArrays(1, &vao); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GenVertexArrays"); |
| try |
| { |
| gl.bindVertexArray(vao); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindVertexArrays"); |
| |
| /* - INVALID_OPERATION when <first> + <count> exceeds limits; */ |
| { |
| GLsizei t_count = n_buffers; |
| GLuint t_first = 0; |
| |
| /* Select first so <first + count> exceeds max, avoid negative first */ |
| if (n_buffers <= max_buffers) |
| { |
| t_first = max_buffers - n_buffers + 1; |
| } |
| else |
| { |
| t_count = max_buffers + 1; |
| /* first = 0; */ |
| } |
| |
| /* Test */ |
| gl.bindVertexBuffers(t_first, t_count, buffer_ids, offsets, strides); |
| CHECK_ERROR(GL_INVALID_OPERATION, "BindVertexBuffers with invalid <first> + <count>"); |
| } |
| |
| /* - INVALID_OPERATION if any value in <buffers> is not zero or the name of |
| * existing buffer; |
| */ |
| { |
| GLuint t_buffer_ids[n_buffers]; |
| |
| memcpy(t_buffer_ids, buffer_ids, sizeof(buffer_ids)); |
| |
| /* Find invalid id */ |
| while (1) |
| { |
| if (GL_TRUE != gl.isBuffer(invalid_id)) |
| { |
| break; |
| } |
| |
| invalid_id += 1; |
| } |
| |
| /* Invalidate the entry */ |
| t_buffer_ids[validated_index] = invalid_id; |
| |
| /* Test */ |
| gl.bindVertexBuffers(first, count, t_buffer_ids, offsets, strides); |
| CHECK_ERROR(GL_INVALID_OPERATION, "BindVertexBuffers with invalid buffer id"); |
| } |
| |
| /* - INVALID_VALUE if any value in <offsets> or <strides> is less than zero. */ |
| { |
| GLintptr t_offsets[n_buffers]; |
| GLsizei t_strides[n_buffers]; |
| |
| memcpy(t_offsets, offsets, sizeof(offsets)); |
| memcpy(t_strides, strides, sizeof(strides)); |
| |
| /* Invalidate the entry */ |
| t_offsets[validated_index] = -1; |
| t_strides[validated_index] = -1; |
| |
| /* Test */ |
| gl.bindVertexBuffers(first, count, buffer_ids, t_offsets, strides); |
| CHECK_ERROR(GL_INVALID_VALUE, "BindVertexBuffers with negative offset"); |
| |
| gl.bindVertexBuffers(first, count, buffer_ids, offsets, t_strides); |
| CHECK_ERROR(GL_INVALID_VALUE, "BindVertexBuffers with negative stride"); |
| } |
| } |
| catch (const std::exception&) |
| { |
| gl.deleteVertexArrays(1, &vao); |
| TCU_FAIL("Unexpected error generated"); |
| } |
| |
| gl.deleteVertexArrays(1, &vao); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "DeleteVertexArrays"); |
| |
| /* Set result */ |
| m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| |
| /* Done */ |
| return tcu::TestNode::STOP; |
| } |
| |
| /** Constructor |
| * |
| * @param context Test context |
| **/ |
| FunctionalBindBuffersBaseTest::FunctionalBindBuffersBaseTest(deqp::Context& context) |
| : TestCase(context, "functional_bind_buffers_base", "Verifies that BindBuffersBase works as expected") |
| { |
| /* Nothing to be done */ |
| } |
| |
| /** Execute test |
| * |
| * @return tcu::TestNode::STOP |
| **/ |
| tcu::TestNode::IterateResult FunctionalBindBuffersBaseTest::iterate() |
| { |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| #if DEBUG_ENBALE_MESSAGE_CALLBACK |
| gl.debugMessageCallback(debug_proc, &m_context); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "DebugMessageCallback"); |
| #endif /* DEBUG_ENBALE_MESSAGE_CALLBACK */ |
| |
| for (size_t i = 0; i < s_n_buffer_tragets; ++i) |
| { |
| const GLenum pname_binding = s_buffer_infos[i].m_pname_binding; |
| const GLenum pname_max = s_buffer_infos[i].m_pname_max; |
| const GLenum pname_max_size = s_buffer_infos[i].m_pname_max_size; |
| const GLenum target = s_buffer_infos[i].m_target; |
| const std::string& target_name = glu::getBufferTargetStr(target).toString(); |
| |
| GLint max_buffers = 0; |
| GLint max_size = 0; |
| |
| /* Get max */ |
| gl.getIntegerv(pname_max, &max_buffers); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetIntegerv"); |
| |
| /* Get max size */ |
| gl.getIntegerv(pname_max_size, &max_size); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetIntegerv"); |
| |
| GLintptr buffer_size = max_size / max_buffers; |
| |
| /* Storage */ |
| std::vector<Buffer> buffer; |
| std::vector<GLuint> buffer_ids; |
| |
| buffer.resize(max_buffers); |
| buffer_ids.resize(max_buffers); |
| |
| /* Prepare buffers */ |
| for (GLint j = 0; j < max_buffers; ++j) |
| { |
| buffer[j].InitData(m_context, target, GL_DYNAMIC_COPY, buffer_size, 0 /* data */); |
| |
| buffer_ids[j] = buffer[j].m_id; |
| } |
| |
| /* |
| * - execute BindBufferBase to bind all buffers to tested target; |
| * - inspect if bindings were modified; |
| */ |
| gl.bindBuffersBase(target, 0 /* first */, max_buffers /* count */, &buffer_ids[0]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindBuffersBase"); |
| |
| for (GLint j = 0; j < max_buffers; ++j) |
| { |
| checkBinding(m_context, pname_binding, j, target_name, buffer_ids[j]); |
| } |
| |
| /* |
| * |
| * - execute BindBufferBase for first half of bindings with NULL as <buffers> |
| * to unbind first half of bindings for tested target; |
| * - inspect if bindings were modified; |
| * - execute BindBufferBase for second half of bindings with NULL as <buffers> |
| * to unbind rest of bindings; |
| * - inspect if bindings were modified; |
| */ |
| GLint half_index = max_buffers / 2; |
| gl.bindBuffersBase(target, 0 /* first */, half_index /* count */, 0); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindBuffersBase"); |
| |
| for (GLint j = 0; j < half_index; ++j) |
| { |
| checkBinding(m_context, pname_binding, j, target_name, 0); |
| } |
| |
| for (GLint j = half_index; j < max_buffers; ++j) |
| { |
| checkBinding(m_context, pname_binding, j, target_name, buffer_ids[j]); |
| } |
| |
| gl.bindBuffersBase(target, half_index /* first */, max_buffers - half_index /* count */, 0); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindBuffersBase"); |
| |
| for (GLint j = 0; j < max_buffers; ++j) |
| { |
| checkBinding(m_context, pname_binding, j, target_name, 0); |
| } |
| |
| /* |
| * - change <buffers> so first entry is invalid; |
| * - execute BindBufferBase to bind all buffers to tested target; It is |
| * expected that INVALID_OPERATION will be generated; |
| * - inspect if all bindings but first were modified; |
| */ |
| |
| /* Find invalid id */ |
| GLuint invalid_id = 1; |
| while (1) |
| { |
| if (GL_TRUE != gl.isBuffer(invalid_id)) |
| { |
| break; |
| } |
| |
| invalid_id += 1; |
| } |
| |
| buffer_ids[0] = invalid_id; |
| |
| gl.bindBuffersBase(target, 0 /* first */, max_buffers /* count */, &buffer_ids[0]); |
| CHECK_ERROR(GL_INVALID_OPERATION, "BindBufferBase with invalid buffer id"); |
| |
| /* Update buffer_ids */ |
| buffer_ids[0] = 0; /* 0 means unbound */ |
| |
| for (GLint j = 0; j < max_buffers; ++j) |
| { |
| checkBinding(m_context, pname_binding, j, target_name, buffer_ids[j]); |
| } |
| |
| /* |
| * - bind any buffer to first binding; |
| * - execute BindBufferBase for 0 as <first>, 1 as <count> and <buffers> filled |
| * with zeros to unbind 1st binding for tested target; |
| * - inspect if bindings were modified; |
| */ |
| gl.bindBufferBase(target, 0, buffer[0].m_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindBufferBase"); |
| checkBinding(m_context, pname_binding, 0, target_name, buffer[0].m_id); |
| |
| std::vector<GLuint> t_buffer_ids; |
| t_buffer_ids.resize(max_buffers); |
| |
| gl.bindBuffersBase(target, 0 /* first */, 1 /* count */, &t_buffer_ids[0]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindBuffersBase"); |
| |
| for (GLint j = 0; j < max_buffers; ++j) |
| { |
| checkBinding(m_context, pname_binding, j, target_name, buffer_ids[j]); |
| } |
| |
| /* - unbind all buffers. */ |
| gl.bindBuffersBase(target, 0 /* first */, max_buffers /* count */, 0); |
| } |
| |
| /* Set result */ |
| m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| |
| /* Done */ |
| return tcu::TestNode::STOP; |
| } |
| |
| /** Constructor |
| * |
| * @param context Test context |
| **/ |
| FunctionalBindBuffersRangeTest::FunctionalBindBuffersRangeTest(deqp::Context& context) |
| : TestCase(context, "functional_bind_buffers_range", "Verifies that BindBuffersRange works as expected") |
| { |
| /* Nothing to be done */ |
| } |
| |
| /** Execute test |
| * |
| * @return tcu::TestNode::STOP |
| **/ |
| tcu::TestNode::IterateResult FunctionalBindBuffersRangeTest::iterate() |
| { |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| #if DEBUG_ENBALE_MESSAGE_CALLBACK |
| gl.debugMessageCallback(debug_proc, &m_context); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "DebugMessageCallback"); |
| #endif /* DEBUG_ENBALE_MESSAGE_CALLBACK */ |
| |
| for (size_t i = 0; i < s_n_buffer_tragets; ++i) |
| { |
| const GLenum pname_binding = s_buffer_infos[i].m_pname_binding; |
| const GLenum pname_max = s_buffer_infos[i].m_pname_max; |
| const GLenum pname_max_size = s_buffer_infos[i].m_pname_max_size; |
| const GLenum target = s_buffer_infos[i].m_target; |
| const std::string& target_name = glu::getBufferTargetStr(target).toString(); |
| |
| GLint max_buffers = 0; |
| GLint max_size = 0; |
| |
| /* Get max */ |
| gl.getIntegerv(pname_max, &max_buffers); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetIntegerv"); |
| |
| /* Get max size */ |
| gl.getIntegerv(pname_max_size, &max_size); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetIntegerv"); |
| |
| GLintptr buffer_size = max_size / max_buffers; |
| |
| /* Storage */ |
| std::vector<Buffer> buffer; |
| std::vector<GLuint> buffer_ids; |
| std::vector<GLintptr> offsets; |
| std::vector<GLsizeiptr> sizes; |
| |
| buffer.resize(max_buffers); |
| buffer_ids.resize(max_buffers); |
| offsets.resize(max_buffers); |
| sizes.resize(max_buffers); |
| |
| /* Prepare buffers */ |
| for (GLint j = 0; j < max_buffers; ++j) |
| { |
| buffer[j].InitData(m_context, target, GL_DYNAMIC_COPY, buffer_size, 0 /* data */); |
| |
| buffer_ids[j] = buffer[j].m_id; |
| offsets[j] = 0; |
| sizes[j] = buffer_size; |
| } |
| |
| /* |
| * - execute BindBufferBase to bind all buffers to tested target; |
| * - inspect if bindings were modified; |
| */ |
| gl.bindBuffersRange(target, 0 /* first */, max_buffers /* count */, &buffer_ids[0], &offsets[0], &sizes[0]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindBuffersRange"); |
| |
| for (GLint j = 0; j < max_buffers; ++j) |
| { |
| checkBinding(m_context, pname_binding, j, target_name, buffer_ids[j]); |
| } |
| |
| /* |
| * |
| * - execute BindBufferBase for first half of bindings with NULL as <buffers> |
| * to unbind first half of bindings for tested target; |
| * - inspect if bindings were modified; |
| * - execute BindBufferBase for second half of bindings with NULL as <buffers> |
| * to unbind rest of bindings; |
| * - inspect if bindings were modified; |
| */ |
| GLint half_index = max_buffers / 2; |
| gl.bindBuffersRange(target, 0 /* first */, half_index /* count */, 0, &offsets[0], &sizes[0]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindBuffersRange"); |
| |
| for (GLint j = 0; j < half_index; ++j) |
| { |
| checkBinding(m_context, pname_binding, j, target_name, 0); |
| } |
| |
| for (GLint j = half_index; j < max_buffers; ++j) |
| { |
| checkBinding(m_context, pname_binding, j, target_name, buffer_ids[j]); |
| } |
| |
| gl.bindBuffersRange(target, half_index /* first */, max_buffers - half_index /* count */, 0, &offsets[0], |
| &sizes[0]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindBuffersRange"); |
| |
| for (GLint j = 0; j < max_buffers; ++j) |
| { |
| checkBinding(m_context, pname_binding, j, target_name, 0); |
| } |
| |
| /* |
| * - change <buffers> so first entry is invalid; |
| * - execute BindBufferBase to bind all buffers to tested target; It is |
| * expected that INVALID_OPERATION will be generated; |
| * - inspect if all bindings but first were modified; |
| */ |
| |
| /* Find invalid id */ |
| GLuint invalid_id = 1; |
| while (1) |
| { |
| if (GL_TRUE != gl.isBuffer(invalid_id)) |
| { |
| break; |
| } |
| |
| invalid_id += 1; |
| } |
| |
| buffer_ids[0] = invalid_id; |
| |
| gl.bindBuffersRange(target, 0 /* first */, max_buffers /* count */, &buffer_ids[0], &offsets[0], &sizes[0]); |
| CHECK_ERROR(GL_INVALID_OPERATION, "BindBuffersRange with invalid buffer id"); |
| |
| /* Update buffer_ids */ |
| buffer_ids[0] = 0; /* 0 means unbound */ |
| |
| for (GLint j = 0; j < max_buffers; ++j) |
| { |
| checkBinding(m_context, pname_binding, j, target_name, buffer_ids[j]); |
| } |
| |
| /* |
| * - bind any buffer to first binding; |
| * - execute BindBufferBase for 0 as <first>, 1 as <count> and <buffers> filled |
| * with zeros to unbind 1st binding for tested target; |
| * - inspect if bindings were modified; |
| */ |
| gl.bindBufferBase(target, 0, buffer[0].m_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindBufferBase"); |
| checkBinding(m_context, pname_binding, 0, target_name, buffer[0].m_id); |
| |
| std::vector<GLuint> t_buffer_ids; |
| t_buffer_ids.resize(max_buffers); |
| |
| gl.bindBuffersRange(target, 0 /* first */, 1 /* count */, &t_buffer_ids[0], &offsets[0], &sizes[0]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindBuffersRange"); |
| |
| for (GLint j = 0; j < max_buffers; ++j) |
| { |
| checkBinding(m_context, pname_binding, j, target_name, buffer_ids[j]); |
| } |
| |
| /* - unbind all buffers. */ |
| gl.bindBuffersBase(target, 0 /* first */, max_buffers /* count */, 0); |
| } |
| |
| /* Set result */ |
| m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| |
| /* Done */ |
| return tcu::TestNode::STOP; |
| } |
| |
| /** Constructor |
| * |
| * @param context Test context |
| **/ |
| FunctionalBindTexturesTest::FunctionalBindTexturesTest(deqp::Context& context) |
| : TestCase(context, "functional_bind_textures", "Verifies that BindTextures works as expected") |
| { |
| /* Nothing to be done */ |
| } |
| |
| /** Execute test |
| * |
| * @return tcu::TestNode::STOP |
| **/ |
| tcu::TestNode::IterateResult FunctionalBindTexturesTest::iterate() |
| { |
| static const GLuint depth = 6; |
| static const GLuint height = 6; |
| static const GLuint width = 6; |
| |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| #if DEBUG_ENBALE_MESSAGE_CALLBACK |
| gl.debugMessageCallback(debug_proc, &m_context); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "DebugMessageCallback"); |
| #endif /* DEBUG_ENBALE_MESSAGE_CALLBACK */ |
| |
| GLuint invalid_id = 1; /* Start with 1, as 0 is not valid name */ |
| GLint max_textures = 0; |
| |
| /* Get max */ |
| gl.getIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_textures); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetIntegerv"); |
| |
| /* Storage */ |
| Buffer buffer; |
| std::vector<Texture> texture; |
| std::vector<GLuint> texture_ids; |
| std::vector<GLuint> t_texture_ids; |
| |
| texture.resize(max_textures); |
| texture_ids.resize(max_textures); |
| t_texture_ids.resize(max_textures); |
| |
| /* Prepare buffer */ |
| buffer.InitData(m_context, GL_TEXTURE_BUFFER, GL_DYNAMIC_COPY, 16 /* size */, 0 /* data */); |
| |
| /* Prepare textures */ |
| for (size_t i = 0; i < s_n_texture_tragets; ++i) |
| { |
| const GLenum target = s_texture_infos[i].m_target; |
| |
| if (GL_TEXTURE_BUFFER != target) |
| { |
| texture[i].InitStorage(m_context, target, 1, GL_RGBA8, width, height, depth); |
| } |
| else |
| { |
| texture[i].InitBuffer(m_context, GL_RGBA8, buffer.m_id); |
| } |
| |
| /* Unbind */ |
| Texture::Bind(gl, 0, target); |
| } |
| |
| for (GLint i = s_n_texture_tragets; i < max_textures; ++i) |
| { |
| texture[i].InitStorage(m_context, GL_TEXTURE_2D, 1, GL_RGBA8, width, height, depth); |
| } |
| |
| /* Unbind */ |
| Texture::Bind(gl, 0, GL_TEXTURE_2D); |
| |
| for (GLint i = 0; i < max_textures; ++i) |
| { |
| texture_ids[i] = texture[i].m_id; |
| } |
| |
| /* |
| * - execute BindTextures to bind all textures; |
| * - inspect bindings of all texture units to verify that proper bindings were |
| * set; |
| */ |
| gl.bindTextures(0, max_textures, &texture_ids[0]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindTextures"); |
| |
| for (GLint i = 0; i < max_textures; ++i) |
| { |
| checkTextureBinding(m_context, getBinding(i), i, texture_ids[i]); |
| } |
| |
| /* |
| * - execute BindTextures for the first half of units with <textures> filled |
| * with zeros, to unbind those units; |
| * - inspect bindings of all texture units to verify that proper bindings were |
| * unbound; |
| */ |
| GLint half_index = max_textures / 2; |
| |
| for (GLint i = 0; i < max_textures; ++i) |
| { |
| t_texture_ids[i] = 0; |
| } |
| |
| gl.bindTextures(0, half_index, &t_texture_ids[0]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindTextures"); |
| |
| for (GLint i = 0; i < half_index; ++i) |
| { |
| checkTextureBinding(m_context, getBinding(i), i, 0); |
| } |
| |
| for (GLint i = half_index; i < max_textures; ++i) |
| { |
| checkTextureBinding(m_context, getBinding(i), i, texture_ids[i]); |
| } |
| |
| /* |
| * - execute BindTextures for the second half of units with NULL as<textures>, |
| * to unbind those units; |
| * - inspect bindings of all texture units to verify that proper bindings were |
| * unbound; |
| */ |
| gl.bindTextures(half_index, max_textures - half_index, 0); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindTextures"); |
| |
| for (GLint i = 0; i < max_textures; ++i) |
| { |
| checkTextureBinding(m_context, getBinding(i), i, 0); |
| } |
| |
| /* |
| * - modify <textures> so first entry is invalid; |
| * - execute BindTextures to bind all textures; It is expected that |
| * INVALID_OPERATION will be generated; |
| * - inspect bindings of all texture units to verify that proper bindings were |
| * set; |
| */ |
| |
| /* Find invalid id */ |
| while (1) |
| { |
| if (GL_TRUE != gl.isTexture(invalid_id)) |
| { |
| break; |
| } |
| |
| invalid_id += 1; |
| } |
| |
| /* Set invalid id */ |
| texture_ids[0] = invalid_id; |
| |
| gl.bindTextures(0, max_textures, &texture_ids[0]); |
| CHECK_ERROR(GL_INVALID_OPERATION, "BindTextures with invalid texture id"); |
| |
| checkTextureBinding(m_context, getBinding(0), 0, 0); |
| for (GLint i = 1; i < max_textures; ++i) |
| { |
| checkTextureBinding(m_context, getBinding(i), i, texture_ids[i]); |
| } |
| |
| /* - unbind all textures. */ |
| gl.bindTextures(0, max_textures, 0); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindTextures"); |
| |
| /* Set result */ |
| m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| |
| /* Done */ |
| return tcu::TestNode::STOP; |
| } |
| |
| /** Constructor |
| * |
| * @param context Test context |
| **/ |
| FunctionalBindSamplersTest::FunctionalBindSamplersTest(deqp::Context& context) |
| : TestCase(context, "functional_bind_samplers", "Verifies that BindSamplers works as expected") |
| { |
| /* Nothing to be done */ |
| } |
| |
| /** Execute test |
| * |
| * @return tcu::TestNode::STOP |
| **/ |
| tcu::TestNode::IterateResult FunctionalBindSamplersTest::iterate() |
| { |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| #if DEBUG_ENBALE_MESSAGE_CALLBACK |
| gl.debugMessageCallback(debug_proc, &m_context); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "DebugMessageCallback"); |
| #endif /* DEBUG_ENBALE_MESSAGE_CALLBACK */ |
| |
| GLuint invalid_id = 1; /* Start with 1, as 0 is not valid name */ |
| GLint max_samplers = 0; |
| |
| /* Get max */ |
| gl.getIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_samplers); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetIntegerv"); |
| |
| /* Storage */ |
| std::vector<GLuint> sampler_ids; |
| std::vector<GLuint> t_sampler_ids; |
| |
| sampler_ids.resize(max_samplers); |
| t_sampler_ids.resize(max_samplers); |
| |
| for (GLint i = 0; i < max_samplers; ++i) |
| { |
| t_sampler_ids[i] = 0; |
| } |
| |
| /* Prepare samplers */ |
| gl.genSamplers(max_samplers, &sampler_ids[0]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GenSamplers"); |
| |
| try |
| { |
| /* - execute BindSamplers to bind all samplers; |
| * - inspect bindings to verify that proper samplers were set; |
| */ |
| gl.bindSamplers(0 /* first */, max_samplers /* count */, &sampler_ids[0]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindSamplers"); |
| |
| for (GLint i = 0; i < max_samplers; ++i) |
| { |
| checkBinding(m_context, GL_SAMPLER_BINDING, i, "Sampler", sampler_ids[i]); |
| } |
| |
| /* - execute BindSamplers for first half of bindings with <samplers> filled |
| * with zeros, to unbind those samplers; |
| * - inspect bindings to verify that proper samplers were unbound; |
| */ |
| GLint half_index = max_samplers / 2; |
| |
| gl.bindSamplers(0, half_index, &t_sampler_ids[0]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindSamplers"); |
| |
| for (GLint i = 0; i < half_index; ++i) |
| { |
| checkBinding(m_context, GL_SAMPLER_BINDING, i, "Sampler", 0); |
| } |
| |
| for (GLint i = half_index; i < max_samplers; ++i) |
| { |
| checkBinding(m_context, GL_SAMPLER_BINDING, i, "Sampler", sampler_ids[i]); |
| } |
| |
| /* - execute BindSamplers for second half of bindings with NULL as <samplers>, |
| * to unbind those samplers; |
| * - inspect bindings to verify that proper samplers were unbound; |
| */ |
| gl.bindSamplers(half_index, max_samplers - half_index, 0); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindSamplers"); |
| |
| for (GLint i = 0; i < max_samplers; ++i) |
| { |
| checkBinding(m_context, GL_SAMPLER_BINDING, i, "Sampler", 0); |
| } |
| |
| /* - modify <samplers> so first entry is invalid; |
| * - execute BindSamplers to bind all samplers; It is expected that |
| * INVALID_OPERATION will be generated; |
| * - inspect bindings to verify that proper samplers were set; |
| */ |
| |
| /* Find invalid id */ |
| while (1) |
| { |
| if (GL_TRUE != gl.isSampler(invalid_id)) |
| { |
| break; |
| } |
| |
| invalid_id += 1; |
| } |
| |
| /* Prepare ids */ |
| t_sampler_ids[0] = invalid_id; |
| |
| for (GLint i = 1; i < max_samplers; ++i) |
| { |
| t_sampler_ids[i] = sampler_ids[i]; |
| } |
| |
| /* Bind */ |
| gl.bindSamplers(0, max_samplers, &t_sampler_ids[0]); |
| CHECK_ERROR(GL_INVALID_OPERATION, "BindSamplers with invalid sampler id"); |
| |
| /* Set 0 for invalid entry */ |
| t_sampler_ids[0] = 0; |
| |
| for (GLint i = 0; i < max_samplers; ++i) |
| { |
| checkBinding(m_context, GL_SAMPLER_BINDING, i, "Sampler", t_sampler_ids[i]); |
| } |
| |
| /* - unbind all samplers. */ |
| gl.bindSamplers(0, max_samplers, 0); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindSamplers"); |
| } |
| catch (const std::exception&) |
| { |
| gl.deleteSamplers(max_samplers, &sampler_ids[0]); |
| |
| TCU_FAIL("Invalid error generated"); |
| } |
| |
| /* Delete samplers */ |
| gl.deleteSamplers(max_samplers, &sampler_ids[0]); |
| |
| /* Set result */ |
| m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| |
| /* Done */ |
| return tcu::TestNode::STOP; |
| } |
| |
| /** Constructor |
| * |
| * @param context Test context |
| **/ |
| FunctionalBindImageTexturesTest::FunctionalBindImageTexturesTest(deqp::Context& context) |
| : TestCase(context, "functional_bind_image_textures", "Verifies that BindImageTextures works as expected") |
| { |
| /* Nothing to be done */ |
| } |
| |
| /** Execute test |
| * |
| * @return tcu::TestNode::STOP |
| **/ |
| tcu::TestNode::IterateResult FunctionalBindImageTexturesTest::iterate() |
| { |
| static const GLuint depth = 6; |
| static const GLuint height = 6; |
| static const GLuint width = 6; |
| |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| #if DEBUG_ENBALE_MESSAGE_CALLBACK |
| gl.debugMessageCallback(debug_proc, &m_context); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "DebugMessageCallback"); |
| #endif /* DEBUG_ENBALE_MESSAGE_CALLBACK */ |
| |
| GLuint invalid_id = 1; /* Start with 1, as 0 is not valid name */ |
| GLint max_textures = 0; |
| |
| /* Get max */ |
| gl.getIntegerv(GL_MAX_IMAGE_UNITS, &max_textures); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetIntegerv"); |
| |
| /* Storage */ |
| Buffer buffer; |
| std::vector<Texture> texture; |
| std::vector<GLuint> texture_ids; |
| std::vector<GLuint> t_texture_ids; |
| |
| texture.resize(max_textures); |
| texture_ids.resize(max_textures); |
| t_texture_ids.resize(max_textures); |
| |
| /* Prepare buffer */ |
| buffer.InitData(m_context, GL_TEXTURE_BUFFER, GL_DYNAMIC_COPY, 16 /* size */, 0 /* data */); |
| |
| /* Prepare textures */ |
| for (GLint i = 0; i < (GLint)s_n_texture_tragets; ++i) |
| { |
| const GLenum target = s_texture_infos[i].m_target; |
| |
| if (i >= max_textures) |
| { |
| break; |
| } |
| |
| if (GL_TEXTURE_BUFFER != target) |
| { |
| texture[i].InitStorage(m_context, target, 1, GL_RGBA8, width, height, depth); |
| } |
| else |
| { |
| texture[i].InitBuffer(m_context, GL_RGBA8, buffer.m_id); |
| } |
| |
| /* Unbind */ |
| Texture::Bind(gl, 0, target); |
| } |
| |
| for (GLint i = (GLint)s_n_texture_tragets; i < max_textures; ++i) |
| { |
| texture[i].InitStorage(m_context, GL_TEXTURE_2D, 1, GL_RGBA8, width, height, depth); |
| } |
| |
| /* Unbind */ |
| Texture::Bind(gl, 0, GL_TEXTURE_2D); |
| |
| for (GLint i = 0; i < max_textures; ++i) |
| { |
| texture_ids[i]
|