| /*------------------------------------------------------------------------- |
| * 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 gl4cBufferStorageTests.cpp |
| * \brief Implements conformance tests for "Buffer storage" functionality. |
| */ /*-------------------------------------------------------------------*/ |
| |
| #include "gl4cBufferStorageTests.hpp" |
| |
| #include "gluContextInfo.hpp" |
| #include "gluDefs.hpp" |
| #include "gluStrUtil.hpp" |
| #include "glwEnums.hpp" |
| #include "glwFunctions.hpp" |
| #include "tcuImageIO.hpp" |
| #include "tcuTestLog.hpp" |
| #include "tcuTexture.hpp" |
| |
| #include <iomanip> |
| #include <string> |
| |
| using namespace glw; |
| |
| namespace gl4cts |
| { |
| namespace BufferStorage |
| { |
| /* Enums */ |
| |
| /* Represents how functionality is supported */ |
| enum FUNCTIONALITY_SUPPORT |
| { |
| FUNCTIONALITY_SUPPORT_NONE = 0, |
| FUNCTIONALITY_SUPPORT_EXTENSION, |
| FUNCTIONALITY_SUPPORT_CORE, |
| FUNCTIONALITY_SUPPORT_NOT_DETERMINED, |
| }; |
| |
| /* Prototypes of functions */ |
| FUNCTIONALITY_SUPPORT getDirectStateAccessSupport(deqp::Context& context); |
| bool isGLVersionAtLeast(const glw::Functions& gl, glw::GLint required_major, glw::GLint required_minor); |
| |
| /* Classes */ |
| |
| /** Represents buffer instance |
| * Provides basic buffer functionality |
| **/ |
| class Buffer |
| { |
| public: |
| // I don't quite understand how the old code *ever* worked... |
| // This is uglyish hack to make it actually compile on any sane |
| // compiler, and not crash. |
| struct MoveMapOwner |
| { |
| MoveMapOwner(Buffer* buffer_, glw::GLvoid* data_) : buffer(buffer_), data(data_) |
| { |
| } |
| |
| Buffer* buffer; |
| glw::GLvoid* data; |
| }; |
| |
| /* Public classes */ |
| class MapOwner |
| { |
| friend class Buffer; |
| |
| public: |
| MapOwner(MapOwner& map_owner); |
| MapOwner(const MoveMapOwner& moveOwner); |
| ~MapOwner(); |
| |
| glw::GLvoid* m_data; |
| |
| private: |
| MapOwner(Buffer& buffer, glw::GLvoid* data); |
| |
| Buffer* m_buffer; |
| }; |
| |
| /* Public methods */ |
| /* Ctr & Dtr */ |
| Buffer(deqp::Context& context); |
| ~Buffer(); |
| |
| /* Init & Release */ |
| void InitData(glw::GLenum target, glw::GLenum usage, glw::GLsizeiptr size, const glw::GLvoid* data); |
| |
| void InitStorage(glw::GLenum target, glw::GLenum flags, glw::GLsizeiptr size, const glw::GLvoid* data); |
| |
| void Release(); |
| |
| /* Functionality */ |
| void Bind() const; |
| void BindBase(glw::GLuint index) const; |
| |
| void BindRange(glw::GLuint index, glw::GLintptr offset, glw::GLsizeiptr size) const; |
| |
| MoveMapOwner MapRange(glw::GLintptr offset, glw::GLsizeiptr length, glw::GLenum access); |
| |
| void UnMap(); |
| |
| /* Public static routines */ |
| /* Extensions */ |
| static void LoadExtDirectStateAccess(deqp::Context& context); |
| |
| /* 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 BindRange(const glw::Functions& gl, glw::GLuint id, glw::GLenum target, glw::GLuint index, |
| glw::GLintptr offset, glw::GLsizeiptr size); |
| |
| 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 GetNamedParameter(const glw::Functions& gl, glw::GLuint buffer, glw::GLenum pname, glw::GLint* data); |
| |
| static void GetParameter(const glw::Functions& gl, glw::GLenum target, glw::GLenum value, glw::GLint* data); |
| |
| static void GetSubData(const glw::Functions& gl, glw::GLenum target, glw::GLintptr offset, glw::GLsizeiptr size, |
| glw::GLvoid* data); |
| |
| static void* Map(const glw::Functions& gl, glw::GLenum target, glw::GLenum access); |
| |
| static void* MapRange(const glw::Functions& gl, glw::GLenum target, glw::GLintptr offset, glw::GLsizeiptr length, |
| glw::GLenum access); |
| |
| static void Storage(const glw::Functions& gl, glw::GLenum target, glw::GLenum flags, glw::GLsizeiptr size, |
| const glw::GLvoid* data); |
| |
| static void SubData(const glw::Functions& gl, glw::GLenum target, glw::GLintptr offset, glw::GLsizeiptr size, |
| glw::GLvoid* data); |
| |
| static void UnMap(const glw::Functions& gl, glw::GLenum target); |
| |
| /* Public fields */ |
| glw::GLuint m_id; |
| |
| /* Public constants */ |
| static const glw::GLuint m_invalid_id; |
| static const glw::GLuint m_n_targets = 13; |
| static const glw::GLenum m_targets[m_n_targets]; |
| |
| 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::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(); |
| |
| /* 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 types */ |
| /* Public methods */ |
| /* Ctr & Dtr */ |
| Texture(deqp::Context& context); |
| ~Texture(); |
| |
| /* Init & Release */ |
| void Release(); |
| |
| /* Public static routines */ |
| /* Extensions */ |
| static void LoadExtDirectStateAccess(deqp::Context& context); |
| |
| /* 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::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); |
| |
| 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; |
| |
| /* Private static fields */ |
| static FUNCTIONALITY_SUPPORT m_direct_state_access_support; |
| }; |
| |
| /** Represents Vertex array object |
| * Provides basic functionality |
| **/ |
| class VertexArray |
| { |
| public: |
| /* Public methods */ |
| /* Ctr & Dtr */ |
| VertexArray(deqp::Context& Context); |
| ~VertexArray(); |
| |
| /* Init & Release */ |
| void Release(); |
| |
| /* Public static methods */ |
| static void Bind(const glw::Functions& gl, 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; |
| }; |
| |
| /* Global variables */ |
| static FUNCTIONALITY_SUPPORT m_direct_state_access_support = FUNCTIONALITY_SUPPORT_NOT_DETERMINED; |
| |
| /* Implementations of functions */ |
| /** Get support for direct state access |
| * |
| * @param context CTS context |
| **/ |
| FUNCTIONALITY_SUPPORT getDirectStateAccessSupport(deqp::Context& context) |
| { |
| if (FUNCTIONALITY_SUPPORT_NOT_DETERMINED == m_direct_state_access_support) |
| { |
| const Functions& gl = context.getRenderContext().getFunctions(); |
| |
| if (true == isGLVersionAtLeast(gl, 4, 5)) |
| { |
| m_direct_state_access_support = FUNCTIONALITY_SUPPORT_CORE; |
| } |
| else |
| { |
| bool is_supported = context.getContextInfo().isExtensionSupported("GL_ARB_direct_state_access"); |
| |
| if (true == is_supported) |
| { |
| m_direct_state_access_support = FUNCTIONALITY_SUPPORT_EXTENSION; |
| } |
| else |
| { |
| m_direct_state_access_support = FUNCTIONALITY_SUPPORT_NONE; |
| } |
| } |
| } |
| |
| return m_direct_state_access_support; |
| } |
| |
| /** Check if GL context meets version requirements |
| * |
| * @param gl Functions |
| * @param required_major Minimum required MAJOR_VERSION |
| * @param required_minor Minimum required MINOR_VERSION |
| * |
| * @return true if GL context version is at least as requested, false otherwise |
| **/ |
| bool isGLVersionAtLeast(const glw::Functions& gl, glw::GLint required_major, glw::GLint required_minor) |
| { |
| glw::GLint major = 0; |
| glw::GLint minor = 0; |
| |
| gl.getIntegerv(GL_MAJOR_VERSION, &major); |
| gl.getIntegerv(GL_MINOR_VERSION, &minor); |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetIntegerv"); |
| |
| if (major > required_major) |
| { |
| /* Major is higher than required one */ |
| return true; |
| } |
| else if (major == required_major) |
| { |
| if (minor >= required_minor) |
| { |
| /* Major is equal to required one */ |
| /* Minor is higher than or equal to required one */ |
| return true; |
| } |
| else |
| { |
| /* Major is equal to required one */ |
| /* Minor is lower than required one */ |
| return false; |
| } |
| } |
| else |
| { |
| /* Major is lower than required one */ |
| return false; |
| } |
| } |
| |
| /* Buffer constants */ |
| const GLuint Buffer::m_invalid_id = -1; |
| |
| const GLenum Buffer::m_targets[Buffer::m_n_targets] = { |
| GL_ARRAY_BUFFER, /* 0 */ |
| GL_ATOMIC_COUNTER_BUFFER, /* 1 */ |
| GL_COPY_READ_BUFFER, /* 2 */ |
| GL_COPY_WRITE_BUFFER, /* 3 */ |
| GL_DISPATCH_INDIRECT_BUFFER, /* 4 */ |
| GL_DRAW_INDIRECT_BUFFER, /* 5 */ |
| GL_ELEMENT_ARRAY_BUFFER, /* 6 */ |
| GL_PIXEL_PACK_BUFFER, /* 7 */ |
| GL_PIXEL_UNPACK_BUFFER, /* 8 */ |
| GL_QUERY_BUFFER, /* 9 */ |
| GL_SHADER_STORAGE_BUFFER, /* 10 */ |
| GL_TRANSFORM_FEEDBACK_BUFFER, /* 11 */ |
| GL_UNIFORM_BUFFER, /* 12 */ |
| }; |
| |
| /** Constructor. |
| * |
| * @param context CTS context. |
| **/ |
| Buffer::Buffer(deqp::Context& context) : m_id(m_invalid_id), m_context(context), m_target(GL_ARRAY_BUFFER) |
| { |
| } |
| |
| /** Destructor |
| * |
| **/ |
| Buffer::~Buffer() |
| { |
| Release(); |
| } |
| |
| /** Initialize buffer instance |
| * |
| * @param target Buffer target |
| * @param usage Buffer usage enum |
| * @param size <size> parameter |
| * @param data <data> parameter |
| **/ |
| void Buffer::InitData(glw::GLenum target, glw::GLenum usage, glw::GLsizeiptr size, const glw::GLvoid* data) |
| { |
| /* Delete previous buffer instance */ |
| Release(); |
| |
| 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); |
| } |
| |
| /** Initialize buffer instance |
| * |
| * @param target Buffer target |
| * @param usage Buffer usage enum |
| * @param size <size> parameter |
| * @param data <data> parameter |
| **/ |
| void Buffer::InitStorage(glw::GLenum target, glw::GLenum flags, glw::GLsizeiptr size, const glw::GLvoid* data) |
| { |
| /* Delete previous buffer instance */ |
| Release(); |
| |
| m_target = target; |
| |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| Generate(gl, m_id); |
| Bind(gl, m_id, m_target); |
| Storage(gl, m_target, flags, 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 |
| { |
| 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 |
| { |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| BindBase(gl, m_id, m_target, index); |
| } |
| |
| /** Binds range of buffer |
| * |
| * @param index <index> parameter |
| * @param offset <offset> parameter |
| * @param size <size> parameter |
| **/ |
| void Buffer::BindRange(glw::GLuint index, glw::GLintptr offset, glw::GLsizeiptr size) const |
| { |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| BindRange(gl, m_id, m_target, index, offset, size); |
| } |
| |
| /** Maps contents of buffer into CPU space |
| * |
| * @param access Requested access |
| * |
| * @return Pointer to memory region available for CPU |
| **/ |
| Buffer::MoveMapOwner Buffer::MapRange(glw::GLintptr offset, glw::GLsizeiptr length, glw::GLenum access) |
| { |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| Bind(gl, m_id, m_target); |
| |
| void* data = MapRange(gl, m_target, offset, length, access); |
| |
| MoveMapOwner map(this, data); |
| |
| return map; |
| } |
| |
| /** Unmaps contents of buffer |
| * |
| **/ |
| void Buffer::UnMap() |
| { |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| UnMap(gl, m_target); |
| } |
| |
| /** Loads entry points for direct state access extension |
| * |
| * @param context CTS context |
| **/ |
| void Buffer::LoadExtDirectStateAccess(deqp::Context& context) |
| { |
| FUNCTIONALITY_SUPPORT support = getDirectStateAccessSupport(context); |
| |
| switch (support) |
| { |
| case FUNCTIONALITY_SUPPORT_NONE: |
| /* Nothing to be done */ |
| break; |
| case FUNCTIONALITY_SUPPORT_CORE: |
| case FUNCTIONALITY_SUPPORT_EXTENSION: |
| break; |
| default: |
| TCU_FAIL("Invalid enum"); |
| break; |
| } |
| } |
| |
| /** 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"); |
| } |
| |
| /** Binds buffer range |
| * |
| * @param gl GL functions |
| * @param id Id of buffer |
| * @param target Buffer target |
| * @param index <index> parameter |
| * @param offset <offset> parameter |
| * @param size <size> parameter |
| **/ |
| void Buffer::BindRange(const glw::Functions& gl, glw::GLuint id, glw::GLenum target, glw::GLuint index, |
| glw::GLintptr offset, glw::GLsizeiptr size) |
| { |
| gl.bindBufferRange(target, index, id, offset, size); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindBufferRange"); |
| } |
| |
| /** 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; |
| } |
| |
| /** Query parameter of named buffer |
| * |
| * @param gl GL functions |
| * @param buffer Buffer name |
| * @param pname Parameter name |
| * @param data Storage for queried results |
| **/ |
| void Buffer::GetNamedParameter(const glw::Functions& gl, glw::GLuint buffer, glw::GLenum pname, glw::GLint* data) |
| { |
| gl.getNamedBufferParameteriv(buffer, pname, data); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetNameBufferParameteriv"); |
| } |
| |
| /** Query parameter of bound buffer |
| * |
| * @param gl GL functions |
| * @param Target Buffer target |
| * @param pname Parameter name |
| * @param data Storage for queried results |
| **/ |
| void Buffer::GetParameter(const glw::Functions& gl, glw::GLenum target, glw::GLenum value, glw::GLint* data) |
| { |
| gl.getBufferParameteriv(target, value, data); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetBufferParameteriv"); |
| } |
| |
| /** Get contents of buffer's region |
| * |
| * @param gl GL functions |
| * @param target Buffer target |
| * @param offset Offset in buffer |
| * @param size <size> parameter |
| * @param data <data> parameter |
| **/ |
| void Buffer::GetSubData(const glw::Functions& gl, glw::GLenum target, glw::GLintptr offset, glw::GLsizeiptr size, |
| glw::GLvoid* data) |
| { |
| gl.getBufferSubData(target, offset, size, data); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GetBufferSubData"); |
| } |
| |
| /** Maps buffer content |
| * |
| * @param gl GL functions |
| * @param target Buffer target |
| * @param access Access rights for mapped region |
| * |
| * @return Mapped memory |
| **/ |
| void* Buffer::Map(const glw::Functions& gl, glw::GLenum target, glw::GLenum access) |
| { |
| void* result = gl.mapBuffer(target, access); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "MapBuffer"); |
| |
| return result; |
| } |
| |
| /** Maps buffer content |
| * |
| * @param gl GL functions |
| * @param target Buffer target |
| * @param access Access rights for mapped region |
| * |
| * @return Mapped memory |
| **/ |
| void* Buffer::MapRange(const glw::Functions& gl, glw::GLenum target, glw::GLintptr offset, glw::GLsizeiptr length, |
| glw::GLenum access) |
| { |
| void* result = gl.mapBufferRange(target, offset, length, access); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "MapBufferRange"); |
| |
| return result; |
| } |
| |
| /** Allocate immutable memory for buffer and sends initial content |
| * |
| * @param gl GL functions |
| * @param target Buffer target |
| * @param flags Buffer flags |
| * @param size <size> parameter |
| * @param data <data> parameter |
| **/ |
| void Buffer::Storage(const glw::Functions& gl, glw::GLenum target, glw::GLenum flags, glw::GLsizeiptr size, |
| const glw::GLvoid* data) |
| { |
| gl.bufferStorage(target, size, data, flags); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BufferStorage"); |
| } |
| |
| /** 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"); |
| } |
| |
| /** Unmaps buffer |
| * |
| * @param gl GL functions |
| * @param target Buffer target |
| **/ |
| void Buffer::UnMap(const glw::Functions& gl, glw::GLenum target) |
| { |
| gl.unmapBuffer(target); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "UnmapBuffer"); |
| } |
| |
| /** Constructor |
| * Takes ownership of mapped region |
| * |
| * @param buffer Mapped buffer |
| * @param data Mapped data |
| **/ |
| Buffer::MapOwner::MapOwner(Buffer& buffer, glw::GLvoid* data) : m_data(data), m_buffer(&buffer) |
| { |
| /* Nothing to be done */ |
| } |
| |
| Buffer::MapOwner::MapOwner(const Buffer::MoveMapOwner& moveOwner) : m_data(moveOwner.data), m_buffer(moveOwner.buffer) |
| { |
| } |
| |
| /** Move constructor |
| * Transfer ownership of mapped region. |
| * |
| * @param map_owner Map owner |
| **/ |
| Buffer::MapOwner::MapOwner(MapOwner& map_owner) : m_data(map_owner.m_data), m_buffer(map_owner.m_buffer) |
| { |
| map_owner.m_data = 0; |
| map_owner.m_buffer = 0; |
| } |
| |
| /** Destructor |
| * Unmaps buffer |
| **/ |
| Buffer::MapOwner::~MapOwner() |
| { |
| m_data = 0; |
| if (0 != m_buffer) |
| { |
| m_buffer->Bind(); |
| m_buffer->UnMap(); |
| m_buffer = 0; |
| } |
| } |
| |
| /* 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 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::GLuint width, glw::GLuint height) |
| { |
| gl.framebufferTexture(target, attachment, texture_id, 0 /* 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(); |
| } |
| |
| /** 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 */ |
| |
| FUNCTIONALITY_SUPPORT Texture::m_direct_state_access_support = FUNCTIONALITY_SUPPORT_NOT_DETERMINED; |
| |
| /* Texture constants */ |
| const GLuint Texture::m_invalid_id = -1; |
| |
| /** Constructor. |
| * |
| * @param context CTS context. |
| **/ |
| Texture::Texture(deqp::Context& context) : m_id(m_invalid_id), m_context(context) |
| { |
| /* Nothing to done here */ |
| } |
| |
| /** Destructor |
| * |
| **/ |
| Texture::~Texture() |
| { |
| Release(); |
| } |
| |
| /** Release texture instance |
| * |
| **/ |
| 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; |
| } |
| } |
| |
| /** Loads entry points for direct state access extension |
| * |
| * @param context CTS context |
| **/ |
| void Texture::LoadExtDirectStateAccess(deqp::Context& context) |
| { |
| FUNCTIONALITY_SUPPORT support = getDirectStateAccessSupport(context); |
| |
| switch (support) |
| { |
| case FUNCTIONALITY_SUPPORT_NONE: |
| /* Nothing to be done */ |
| break; |
| case FUNCTIONALITY_SUPPORT_CORE: |
| case FUNCTIONALITY_SUPPORT_EXTENSION: |
| break; |
| default: |
| TCU_FAIL("Invalid enum"); |
| break; |
| } |
| } |
| |
| /** 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::GLenum target, glw::GLenum format, glw::GLenum type, |
| glw::GLvoid* out_data) |
| { |
| gl.getTexImage(target, 0 /* 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) |
| { |
| switch (target) |
| { |
| case GL_TEXTURE_1D: |
| gl.texStorage1D(target, levels, internal_format, width); |
| 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); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "TexStorage2D"); |
| break; |
| case GL_TEXTURE_3D: |
| case GL_TEXTURE_2D_ARRAY: |
| gl.texStorage3D(target, levels, internal_format, width, height, depth); |
| 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: |
| 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; |
| } |
| } |
| |
| /* VertexArray constants */ |
| const GLuint VertexArray::m_invalid_id = -1; |
| |
| /** Constructor. |
| * |
| * @param context CTS context. |
| **/ |
| VertexArray::VertexArray(deqp::Context& context) : m_id(m_invalid_id), m_context(context) |
| { |
| } |
| |
| /** Destructor |
| * |
| **/ |
| VertexArray::~VertexArray() |
| { |
| Release(); |
| } |
| |
| /** Release vertex array object instance |
| * |
| **/ |
| void VertexArray::Release() |
| { |
| if (m_invalid_id != m_id) |
| { |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| Bind(gl, 0); |
| |
| gl.deleteVertexArrays(1, &m_id); |
| |
| m_id = m_invalid_id; |
| } |
| } |
| |
| /** Binds Vertex array object |
| * |
| * @param gl GL functions |
| * @param id ID of vertex array object |
| **/ |
| void VertexArray::Bind(const glw::Functions& gl, glw::GLuint id) |
| { |
| gl.bindVertexArray(id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindVertexArray"); |
| } |
| |
| /** Generates Vertex array object |
| * |
| * @param gl GL functions |
| * @param out_id ID of vertex array object |
| **/ |
| void VertexArray::Generate(const glw::Functions& gl, glw::GLuint& out_id) |
| { |
| GLuint id = m_invalid_id; |
| |
| gl.genVertexArrays(1, &id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "GenVertexArrays"); |
| |
| if (m_invalid_id == id) |
| { |
| TCU_FAIL("Invalid id"); |
| } |
| |
| out_id = id; |
| } |
| |
| /** Constructor |
| * |
| * @param context Test context |
| **/ |
| ErrorsTest::ErrorsTest(deqp::Context& context) |
| : TestCase(context, "errors", "Test if errors are generated as specified") |
| { |
| /* Nothing to be done here */ |
| } |
| |
| /** Execute test |
| * |
| * @return tcu::TestNode::STOP otherwise |
| **/ |
| tcu::TestNode::IterateResult ErrorsTest::iterate() |
| { |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| bool test_result = true; |
| |
| Buffer::LoadExtDirectStateAccess(m_context); |
| |
| // No GL45 or GL_ARB_direct_state_access support |
| if (m_direct_state_access_support == FUNCTIONALITY_SUPPORT_NONE) |
| { |
| m_context.getTestContext().setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Unsupported"); |
| return tcu::TestNode::STOP; |
| } |
| |
| /* |
| * - INVALID_OPERATION is generated by BufferStorage when 0 is bound to |
| * <target>; Check all targets; |
| */ |
| for (GLuint i = 0; i < Buffer::m_n_targets; ++i) |
| { |
| const GLenum target = Buffer::m_targets[i]; |
| std::string message = "BufferStorage was executed for id 0, target: "; |
| |
| message.append(glu::getBufferTargetStr(target).toString().c_str()); |
| |
| Buffer::Bind(gl, 0 /* id */, target); |
| gl.bufferStorage(target, 0 /* size */, 0 /* data */, GL_DYNAMIC_STORAGE_BIT /* flags */); |
| |
| verifyError(GL_INVALID_OPERATION, message.c_str(), test_result); |
| } |
| |
| /* |
| * - INVLIAD_OPERATION is generated by BufferStorage, NamedBufferStorage and |
| * BufferData if buffer already have immutable store; |
| */ |
| { |
| static const GLsizeiptr data_size = 32; |
| static GLubyte data[data_size]; |
| |
| Buffer buffer(m_context); |
| buffer.InitStorage(GL_ARRAY_BUFFER, GL_DYNAMIC_STORAGE_BIT, data_size, data); |
| |
| /* NamedBufferStorage */ |
| if (0 != gl.namedBufferStorage) |
| { |
| gl.namedBufferStorage(buffer.m_id, data_size, data, GL_DYNAMIC_STORAGE_BIT); |
| verifyError(GL_INVALID_OPERATION, "NamedBufferStorage was executed for id with immutable storage", |
| test_result); |
| } |
| |
| /* BufferStorage */ |
| Buffer::Bind(gl, buffer.m_id, GL_ARRAY_BUFFER); |
| |
| gl.bufferStorage(GL_ARRAY_BUFFER, data_size, data, GL_DYNAMIC_STORAGE_BIT); |
| verifyError(GL_INVALID_OPERATION, "BufferStorage was executed for target with immutable storage", test_result); |
| |
| Buffer::Bind(gl, 0, GL_ARRAY_BUFFER); |
| } |
| |
| /* |
| * - INVALID_VALUE is generated by BufferStorage and NamedBufferStorage when |
| * <size> is less or equal to zero; |
| */ |
| { |
| static const GLsizeiptr data_size = 32; |
| static GLubyte data[data_size]; |
| |
| Buffer buffer(m_context); |
| gl.createBuffers(1, &buffer.m_id); |
| |
| /* NamedBufferStorage */ |
| if (0 != gl.namedBufferStorage) |
| { |
| gl.namedBufferStorage(buffer.m_id, 0 /* size */, data, GL_DYNAMIC_STORAGE_BIT); |
| verifyError(GL_INVALID_VALUE, "NamedBufferStorage was executed with size == 0", test_result); |
| |
| gl.namedBufferStorage(buffer.m_id, -16 /* size */, data, GL_DYNAMIC_STORAGE_BIT); |
| verifyError(GL_INVALID_VALUE, "NamedBufferStorage was executed with size == -16", test_result); |
| } |
| |
| /* BufferStorage */ |
| Buffer::Bind(gl, buffer.m_id, GL_ARRAY_BUFFER); |
| |
| gl.bufferStorage(GL_ARRAY_BUFFER, 0 /* size */, data, GL_DYNAMIC_STORAGE_BIT); |
| verifyError(GL_INVALID_VALUE, "BufferStorage was executed with size == 0", test_result); |
| |
| gl.bufferStorage(GL_ARRAY_BUFFER, -16 /* size */, data, GL_DYNAMIC_STORAGE_BIT); |
| verifyError(GL_INVALID_VALUE, "BufferStorage was executed with size == -16", test_result); |
| |
| Buffer::Bind(gl, 0, GL_ARRAY_BUFFER); |
| } |
| |
| /* |
| * - INVLAID_VALUE is generated by BufferStorage and NamedBufferStorage when |
| * <flags> contains MAP_PERSISTENT_BIT and neither MAP_READ_BIT nor |
| * MAP_WRITE_BIT; |
| */ |
| { |
| static const GLsizeiptr data_size = 32; |
| static GLubyte data[data_size]; |
| |
| Buffer buffer(m_context); |
| gl.createBuffers(1, &buffer.m_id); |
| |
| /* NamedBufferStorage */ |
| if (0 != gl.namedBufferStorage) |
| { |
| gl.namedBufferStorage(buffer.m_id, data_size, data, GL_MAP_PERSISTENT_BIT); |
| verifyError(GL_INVALID_VALUE, "NamedBufferStorage was executed with flags == GL_MAP_PERSISTENT_BIT", |
| test_result); |
| } |
| |
| /* BufferStorage */ |
| Buffer::Bind(gl, buffer.m_id, GL_ARRAY_BUFFER); |
| |
| gl.bufferStorage(GL_ARRAY_BUFFER, data_size, data, GL_MAP_PERSISTENT_BIT); |
| verifyError(GL_INVALID_VALUE, "BufferStorage was executed with flags == GL_MAP_PERSISTENT_BIT", test_result); |
| |
| Buffer::Bind(gl, 0, GL_ARRAY_BUFFER); |
| } |
| |
| /* |
| * - INVALID_VALUE is generated by BufferStorage and NamedBufferStorage when |
| * <flags> contains MAP_COHERENT_BIT and no MAP_PERSISTENT_BIT; |
| */ |
| { |
| static const GLsizeiptr data_size = 32; |
| static GLubyte data[data_size]; |
| |
| Buffer buffer(m_context); |
| gl.createBuffers(1, &buffer.m_id); |
| |
| /* NamedBufferStorage */ |
| if (0 != gl.namedBufferStorage) |
| { |
| gl.namedBufferStorage(buffer.m_id, data_size, data, GL_MAP_COHERENT_BIT); |
| verifyError(GL_INVALID_VALUE, "NamedBufferStorage was executed with flags == GL_MAP_COHERENT_BIT", |
| test_result); |
| } |
| |
| /* BufferStorage */ |
| Buffer::Bind(gl, buffer.m_id, GL_ARRAY_BUFFER); |
| |
| gl.bufferStorage(GL_ARRAY_BUFFER, data_size, data, GL_MAP_COHERENT_BIT); |
| verifyError(GL_INVALID_VALUE, "BufferStorage was executed with flags == GL_MAP_COHERENT_BIT", test_result); |
| |
| Buffer::Bind(gl, 0, GL_ARRAY_BUFFER); |
| } |
| |
| /* |
| * - INVALID_OPERATION is generated by MapBufferRange if any of: |
| * * MAP_COHERENT_BIT, |
| * * MAP_PERSISTENT_BIT, |
| * * MAP_READ_BIT, |
| * * MAP_WRITE_BIT |
| * is included in <access> and not in buffer's storage flags; |
| */ |
| { |
| static const GLsizeiptr data_size = 32; |
| static GLubyte data[data_size]; |
| |
| Buffer buffer(m_context); |
| buffer.InitStorage(GL_ARRAY_BUFFER, GL_DYNAMIC_STORAGE_BIT, data_size, data); |
| |
| /* MapNamedBufferRange */ |
| if (0 != gl.mapNamedBufferRange) |
| { |
| gl.mapNamedBufferRange(buffer.m_id, 0 /* offset */, data_size, GL_MAP_READ_BIT); |
| verifyError(GL_INVALID_OPERATION, "MapNamedBufferRange was executed with access == GL_MAP_READ_BIT, " |
| "storage flags == GL_DYNAMIC_STORAGE_BIT", |
| test_result); |
| |
| gl.mapNamedBufferRange(buffer.m_id, 0 /* offset */, data_size, GL_MAP_WRITE_BIT); |
| verifyError(GL_INVALID_OPERATION, "MapNamedBufferRange was executed with access == GL_MAP_WRITE_BIT, " |
| "storage flags == GL_DYNAMIC_STORAGE_BIT", |
| test_result); |
| |
| gl.mapNamedBufferRange(buffer.m_id, 0 /* offset */, data_size, GL_MAP_PERSISTENT_BIT); |
| verifyError(GL_INVALID_OPERATION, "MapNamedBufferRange was executed with access == GL_MAP_PERSISTENT_BIT, " |
| "storage flags == GL_DYNAMIC_STORAGE_BIT", |
| test_result); |
| |
| gl.mapNamedBufferRange(buffer.m_id, 0 /* offset */, data_size, GL_MAP_COHERENT_BIT); |
| verifyError(GL_INVALID_OPERATION, "MapNamedBufferRange was executed with access == GL_MAP_COHERENT_BIT, " |
| "storage flags == GL_DYNAMIC_STORAGE_BIT", |
| test_result); |
| } |
| |
| /* BufferStorage */ |
| Buffer::Bind(gl, buffer.m_id, GL_ARRAY_BUFFER); |
| |
| gl.mapBufferRange(GL_ARRAY_BUFFER, 0 /* offset */, data_size, GL_MAP_READ_BIT); |
| verifyError( |
| GL_INVALID_OPERATION, |
| "MapBufferRange was executed with access == GL_MAP_READ_BIT, storage flags == GL_DYNAMIC_STORAGE_BIT", |
| test_result); |
| |
| gl.mapBufferRange(GL_ARRAY_BUFFER, 0 /* offset */, data_size, GL_MAP_WRITE_BIT); |
| verifyError( |
| GL_INVALID_OPERATION, |
| "MapBufferRange was executed with access == GL_MAP_WRITE_BIT, storage flags == GL_DYNAMIC_STORAGE_BIT", |
| test_result); |
| |
| gl.mapBufferRange(GL_ARRAY_BUFFER, 0 /* offset */, data_size, GL_MAP_PERSISTENT_BIT); |
| verifyError( |
| GL_INVALID_OPERATION, |
| "MapBufferRange was executed with access == GL_MAP_PERSISTENT_BIT, storage flags == GL_DYNAMIC_STORAGE_BIT", |
| test_result); |
| |
| gl.mapBufferRange(GL_ARRAY_BUFFER, 0 /* offset */, data_size, GL_MAP_COHERENT_BIT); |
| verifyError( |
| GL_INVALID_OPERATION, |
| "MapBufferRange was executed with access == GL_MAP_COHERENT_BIT, storage flags == GL_DYNAMIC_STORAGE_BIT", |
| test_result); |
| |
| Buffer::Bind(gl, 0, GL_ARRAY_BUFFER); |
| } |
| |
| /* |
| * - INVALID_OPERATION is generated by BufferSubData and NamedBufferSubData |
| * when buffer has immutable store but its flags does not include |
| * DYNAMIC_STORAGE. |
| */ |
| { |
| static const GLsizeiptr data_size = 32; |
| static GLubyte data[data_size]; |
| |
| Buffer buffer(m_context); |
| buffer.InitStorage(GL_ARRAY_BUFFER, GL_MAP_READ_BIT | GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT, data_size, |
| data); |
| |
| /* NamedBufferSubData */ |
| if (0 != gl.namedBufferSubData) |
| { |
| gl.namedBufferSubData(buffer.m_id, 0 /* offset */, data_size, data); |
| verifyError(GL_INVALID_OPERATION, |
| "NamedBufferSubData was executed for storage without GL_DYNAMIC_STORAGE_BIT", test_result); |
| } |
| |
| /* BufferStorage */ |
| Buffer::Bind(gl, buffer.m_id, GL_ARRAY_BUFFER); |
| |
| gl.bufferSubData(GL_ARRAY_BUFFER, 0 /* offset */, data_size, data); |
| verifyError(GL_INVALID_OPERATION, "BufferSubData was executed for storage without GL_DYNAMIC_STORAGE_BIT", |
| test_result); |
| |
| Buffer::Bind(gl, 0, GL_ARRAY_BUFFER); |
| } |
| |
| /* Set result */ |
| if (true == test_result) |
| { |
| m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| } |
| else |
| { |
| m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Fail"); |
| } |
| |
| /* Done */ |
| return tcu::TestNode::STOP; |
| } |
| |
| /** Verifies that expected error was generated |
| * |
| * @param expected_error Expected error |
| * @param error_message Message that will be logged in case of wrong error |
| * @param out_test_result Set to false if worng error was generated, not modified otherwise |
| **/ |
| void ErrorsTest::verifyError(glw::GLenum expected_error, const glw::GLchar* error_message, bool& out_test_result) |
| { |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| const GLenum error = gl.getError(); |
| |
| if (error != expected_error) |
| { |
| out_test_result = false; |
| |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "Got invalid error: " << glu::getErrorName(error) |
| << ", expected: " << glu::getErrorName(expected_error) << ". Message: " << error_message |
| << tcu::TestLog::EndMessage; |
| } |
| } |
| |
| /** Constructor |
| * |
| * @param context Test context |
| **/ |
| GetBufferParameterTest::GetBufferParameterTest(deqp::Context& context) |
| : TestCase(context, "get_buffer_parameter", "Test queries for parameters of buffers") |
| { |
| static const GLenum s_mapping_bits[] = { 0, GL_MAP_PERSISTENT_BIT, GL_MAP_COHERENT_BIT | GL_MAP_PERSISTENT_BIT }; |
| static const GLuint s_n_mapping_bits = sizeof(s_mapping_bits) / sizeof(s_mapping_bits[0]); |
| |
| GLenum flags = 0; |
| |
| for (GLuint dynamic = 0; dynamic < 2; ++dynamic) |
| { |
| flags = (0 == dynamic) ? 0 : GL_DYNAMIC_STORAGE_BIT; |
| |
| for (GLuint client = 0; client < 2; ++client) |
| { |
| flags |= (0 == client) ? 0 : GL_CLIENT_STORAGE_BIT; |
| |
| /* No "map" bits */ |
| if (0 != flags) |
| { |
| m_test_cases.push_back(testCase(flags, 0)); |
| } |
| |
| for (GLuint flag_idx = 0; flag_idx < s_n_mapping_bits; ++flag_idx) |
| { |
| const GLenum flag_mapping_bits = s_mapping_bits[flag_idx]; |
| const GLenum flags_with_mapping = flags | flag_mapping_bits; |
| |
| for (GLuint access_idx = 0; access_idx <= flag_idx; ++access_idx) |
| { |
| const GLenum access = s_mapping_bits[access_idx]; |
| |
| m_test_cases.push_back(testCase(flags_with_mapping | GL_MAP_READ_BIT, access | GL_MAP_READ_BIT)); |
| m_test_cases.push_back(testCase(flags_with_mapping | GL_MAP_WRITE_BIT, access | GL_MAP_WRITE_BIT)); |
| m_test_cases.push_back( |
| testCase(flags_with_mapping | GL_MAP_READ_BIT | GL_MAP_WRITE_BIT, access | GL_MAP_READ_BIT)); |
| m_test_cases.push_back( |
| testCase(flags_with_mapping | GL_MAP_READ_BIT | GL_MAP_WRITE_BIT, access | GL_MAP_WRITE_BIT)); |
| m_test_cases.push_back(testCase(flags_with_mapping | GL_MAP_READ_BIT | GL_MAP_WRITE_BIT, |
| access | GL_MAP_WRITE_BIT | GL_MAP_READ_BIT)); |
| } |
| } |
| } |
| } |
| } |
| |
| /** Execute test |
| * |
| * @return tcu::TestNode::STOP otherwise |
| **/ |
| tcu::TestNode::IterateResult GetBufferParameterTest::iterate() |
| { |
| static const GLsizeiptr data_size = 32; |
| static GLubyte data[data_size]; |
| |
| Buffer::LoadExtDirectStateAccess(m_context); |
| |
| // No GL45 or GL_ARB_direct_state_access support |
| if (m_direct_state_access_support == FUNCTIONALITY_SUPPORT_NONE) |
| { |
| m_context.getTestContext().setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Unsupported"); |
| return tcu::TestNode::STOP; |
| } |
| |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| bool test_result = true; |
| |
| for (GLuint i = 0; i < m_test_cases.size(); ++i) |
| { |
| const testCase& test_case = m_test_cases[i]; |
| const GLenum access = test_case.m_access; |
| const GLenum flags = test_case.m_flags; |
| |
| GLint queried_flags = -1; |
| GLint queried_immutable = -1; |
| GLint queried_size = -1; |
| |
| Buffer buffer(m_context); |
| |
| buffer.InitStorage(GL_ARRAY_BUFFER, flags, data_size, data); |
| Buffer::Bind(gl, buffer.m_id, GL_ARRAY_BUFFER); |
| |
| if (0 != gl.getNamedBufferParameteriv) |
| { |
| Buffer::GetNamedParameter(gl, buffer.m_id, GL_BUFFER_STORAGE_FLAGS, &queried_flags); |
| Buffer::GetNamedParameter(gl, buffer.m_id, GL_BUFFER_IMMUTABLE_STORAGE, &queried_immutable); |
| Buffer::GetNamedParameter(gl, buffer.m_id, GL_BUFFER_SIZE, &queried_size); |
| |
| if (queried_flags != (GLint)flags) |
| { |
| test_result = false; |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message |
| << "GetNamedBufferParameteriv reported invalid state of GL_BUFFER_STORAGE_FLAGS: " << queried_flags |
| << " expected: " << flags << tcu::TestLog::EndMessage; |
| } |
| } |
| |
| if (queried_flags != (GLint)flags) |
| { |
| test_result = false; |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message |
| << "GetNamedBufferParameteriv reported invalid state of GL_BUFFER_STORAGE_FLAGS: " << queried_flags |
| << " expected: " << flags << tcu::TestLog::EndMessage; |
| } |
| |
| if (queried_immutable != GL_TRUE) |
| { |
| test_result = false; |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message |
| << "GetNamedBufferParameteriv reported invalid state of GL_BUFFER_IMMUTABLE_STORAGE: " |
| << queried_immutable << " expected: " << GL_TRUE << tcu::TestLog::EndMessage; |
| } |
| |
| if (queried_size != data_size) |
| { |
| test_result = false; |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message |
| << "GetNamedBufferParameteriv reported invalid state of GL_BUFFER_SIZE: " << queried_size |
| << " expected: " << data_size << tcu::TestLog::EndMessage; |
| } |
| |
| queried_flags = -1; |
| queried_immutable = -1; |
| queried_size = -1; |
| |
| Buffer::GetParameter(gl, GL_ARRAY_BUFFER, GL_BUFFER_STORAGE_FLAGS, &queried_flags); |
| Buffer::GetParameter(gl, GL_ARRAY_BUFFER, GL_BUFFER_IMMUTABLE_STORAGE, &queried_immutable); |
| Buffer::GetParameter(gl, GL_ARRAY_BUFFER, GL_BUFFER_SIZE, &queried_size); |
| |
| if (queried_flags != (GLint)flags) |
| { |
| test_result = false; |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message |
| << "GetBufferParameteriv reported invalid state of GL_BUFFER_STORAGE_FLAGS: " << queried_flags |
| << " expected: " << flags << tcu::TestLog::EndMessage; |
| } |
| |
| if (queried_immutable != GL_TRUE) |
| { |
| test_result = false; |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message |
| << "GetBufferParameteriv reported invalid state of GL_BUFFER_IMMUTABLE_STORAGE: " << queried_immutable |
| << " expected: " << GL_TRUE << tcu::TestLog::EndMessage; |
| } |
| |
| if (queried_size != data_size) |
| { |
| test_result = false; |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message |
| << "GetBufferParameteriv reported invalid state of GL_BUFFER_SIZE: " << queried_size |
| << " expected: " << data_size << tcu::TestLog::EndMessage; |
| } |
| |
| if (0 != access) |
| { |
| GLint queried_access = -1; |
| |
| Buffer::MapOwner tmp(buffer.MapRange(0 /* offset */, data_size, access)); |
| |
| if (0 != gl.getNamedBufferParameteriv) |
| { |
| Buffer::GetNamedParameter(gl, buffer.m_id, GL_BUFFER_ACCESS_FLAGS, &queried_access); |
| } |
| |
| if (queried_access != (GLint)access) |
| { |
| test_result = false; |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message |
| << "GetNamedBufferParameteriv reported invalid state of GL_BUFFER_ACCESS_FLAGS: " << queried_access |
| << " expected: " << access << tcu::TestLog::EndMessage; |
| } |
| |
| queried_access = -1; |
| |
| Buffer::GetParameter(gl, GL_ARRAY_BUFFER, GL_BUFFER_ACCESS_FLAGS, &queried_access); |
| |
| if (queried_access != (GLint)access) |
| { |
| test_result = false; |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message |
| << "GetBufferParameteriv reported invalid state of GL_BUFFER_ACCESS_FLAGS: " << queried_access |
| << " expected: " << access << tcu::TestLog::EndMessage; |
| } |
| } |
| |
| Buffer::Bind(gl, 0 /* id */, GL_ARRAY_BUFFER); |
| } |
| |
| /* Set result */ |
| if (true == test_result) |
| { |
| m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| } |
| else |
| { |
| m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Fail"); |
| } |
| |
| /* Done */ |
| return tcu::TestNode::STOP; |
| } |
| |
| /** Constructor |
| * |
| * @param context Test context |
| **/ |
| GetBufferParameterTest::testCase::testCase(glw::GLenum flags, glw::GLenum access) : m_flags(flags), m_access(access) |
| { |
| } |
| |
| /** Constructor |
| * |
| * @param context Test context |
| **/ |
| DynamicStorageTest::DynamicStorageTest(deqp::Context& context) |
| : TestCase(context, "dynamic_storage", "Test if DYNAMIC_STORAGE_BIT is respected") |
| { |
| /* Nothing to be done here */ |
| } |
| |
| /** Execute test |
| * |
| * @return tcu::TestNode::STOP otherwise |
| **/ |
| tcu::TestNode::IterateResult DynamicStorageTest::iterate() |
| { |
| static const size_t data_size = 64; |
| |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| bool test_result = true; |
| |
| /* |
| * - prepare 64 bytes immutable buffer filled with value 1; Bind the buffer to |
| * COPY_READ_BUFFER; |
| * - prepare 64 bytes immutable buffer filled with value 2; Do not set |
| * DYNAMIC_STORAGE_BIT for <flags>; Bind the buffer to COPY_WRITE_BUFFER; |
| * - execute BufferSubData to update COPY_WRITE_BUFFER buffer with 64 bytes |
| * filled with value 3; INVLIAD_OPERATION error should be generated; |
| * - inspect contents of buffer to verify it is filled with 2; |
| * - execute CopyBufferSubData to transfer data from COPY_READ_BUFFER to |
| * COPY_WRITE_BUFFER; No error should be generated; |
| * - inspect contents of buffer to verify it is filled with 1; |
| */ |
| { |
| /* Prepare buffers */ |
| GLubyte read_data[data_size]; |
| GLubyte temp_data[data_size]; |
| GLubyte update_data[data_size]; |
| GLubyte write_data[data_size]; |
| |
| for (size_t i = 0; i < data_size; ++i) |
| { |
| read_data[i] = 1; |
| temp_data[i] = 0; |
| update_data[i] = 3; |
| write_data[i] = 2; |
| } |
| |
| Buffer read_buffer(m_context); |
| Buffer write_buffer(m_context); |
| |
| read_buffer.InitStorage(GL_COPY_READ_BUFFER, 0 /* flags */, data_size, read_data); |
| write_buffer.InitStorage(GL_COPY_WRITE_BUFFER, 0 /* flags */, data_size, write_data); |
| |
| /* Check bufferSubData */ |
| write_buffer.Bind(); |
| gl.bufferSubData(GL_COPY_WRITE_BUFFER, 0 /* offset */, data_size, update_data); |
| |
| GLenum error = gl.getError(); |
| if (GL_INVALID_OPERATION != error) |
| { |
| test_result = false; |
| |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "Invalid error was generated. BufferSubData was executed on store without " |
| "DYNAMIC_STORAGE_BIT. Expected INVALID_OPERATION, got: " |
| << glu::getErrorStr(error).toString().c_str() << tcu::TestLog::EndMessage; |
| } |
| |
| Buffer::GetSubData(gl, GL_COPY_WRITE_BUFFER, 0 /* offset */, data_size, temp_data); |
| |
| if (0 != memcmp(temp_data, write_data, data_size)) |
| { |
| test_result = false; |
| |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "BufferSubData modified contents of store without DYNAMIC_STORAGE_BIT." |
| << tcu::TestLog::EndMessage; |
| } |
| |
| /* Check copyBufferSubData */ |
| read_buffer.Bind(); |
| gl.copyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0 /* readOffset */, 0 /* writeOffset */, |
| data_size); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "CopyBufferSubData"); |
| |
| Buffer::GetSubData(gl, GL_COPY_WRITE_BUFFER, 0 /* offset */, data_size, temp_data); |
| |
| if (0 != memcmp(temp_data, read_data, data_size)) |
| { |
| test_result = false; |
| |
| m_context.getTestContext().getLog() << tcu::TestLog::Message |
| << "CopyBufferSubData stored invalid contents in write target buffer." |
| << tcu::TestLog::EndMessage; |
| } |
| } |
| |
| /* |
| * - delete buffer and create new one; This time <flags> should contain |
| * DYNAMIC_STORAGE_BIT; Bind the buffer to COPY_WRITE_BUFFER; |
| * - execute BufferSubData to update COPY_WRITE_BUFFER buffer with 64 bytes |
| * filled with value 3; No error should be generated; |
| * - inspect contents of buffer to verify it is filled with 3; |
| */ |
| { |
| /* Prepare buffers */ |
| GLubyte temp_data[data_size]; |
| GLubyte update_data[data_size]; |
| GLubyte write_data[data_size]; |
| |
| for (size_t i = 0; i < data_size; ++i) |
| { |
| temp_data[i] = 0; |
| update_data[i] = 3; |
| write_data[i] = 2; |
| } |
| |
| Buffer write_buffer(m_context); |
| |
| write_buffer.InitStorage(GL_COPY_WRITE_BUFFER, GL_DYNAMIC_STORAGE_BIT, data_size, write_data); |
| |
| /* Check bufferSubData */ |
| write_buffer.Bind(); |
| gl.bufferSubData(GL_COPY_WRITE_BUFFER, 0 /* offset */, data_size, update_data); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BufferSubData"); |
| |
| Buffer::GetSubData(gl, GL_COPY_WRITE_BUFFER, 0 /* offset */, data_size, temp_data); |
| |
| if (0 != memcmp(temp_data, update_data, data_size)) |
| { |
| test_result = false; |
| |
| m_context.getTestContext().getLog() << tcu::TestLog::Message |
| << "BufferSubData stored invalid contents in write target buffer." |
| << tcu::TestLog::EndMessage; |
| } |
| } |
| |
| /* Set result */ |
| if (true == test_result) |
| { |
| m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| } |
| else |
| { |
| m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Fail"); |
| } |
| |
| /* Done */ |
| return tcu::TestNode::STOP; |
| } |
| |
| /** Constructor |
| * |
| * @param context Test context |
| **/ |
| MapPersistentBufferSubDataTest::MapPersistentBufferSubDataTest(deqp::Context& context) |
| : TestCase(context, "map_persistent_buffer_sub_data", "Test sub buffer operations against mapped buffer") |
| { |
| /* Nothing to be done here */ |
| } |
| |
| /** Execute test |
| * |
| * @return tcu::TestNode::STOP otherwise |
| **/ |
| tcu::TestNode::IterateResult MapPersistentBufferSubDataTest::iterate() |
| { |
| static const size_t data_size = 64; |
| static const GLintptr mapped_region_offset = 16; |
| static const GLsizeiptr mapped_region_size = 16; |
| static const testCase test_cases[] = { |
| { 0, 16, false }, /* before mapped region */ |
| { 32, 16, false }, /* after mapped region */ |
| { 8, 16, true }, /* at the beginning */ |
| { 24, 16, true }, /* at the end */ |
| { 12, 8, true }, /* in the middle */ |
| }; |
| static const size_t n_test_cases = sizeof(test_cases) / sizeof(test_cases[0]); |
| |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| bool test_result = true; |
| |
| /* Storage for data */ |
| GLubyte incrementing_data[data_size]; |
| |
| /* Prepare data */ |
| for (size_t i = 0; i < data_size; ++i) |
| { |
| incrementing_data[i] = (glw::GLubyte)i; |
| } |
| |
| /* Load DSA */ |
| Buffer::LoadExtDirectStateAccess(m_context); |
| |
| // No GL45 or GL_ARB_direct_state_access support |
| if (m_direct_state_access_support == FUNCTIONALITY_SUPPORT_NONE) |
| { |
| m_context.getTestContext().setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Unsupported"); |
| return tcu::TestNode::STOP; |
| } |
| |
| /* Prepare buffer */ |
| Buffer buffer(m_context); |
| buffer.InitStorage(GL_ARRAY_BUFFER, |
| GL_DYNAMIC_STORAGE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_READ_BIT | GL_MAP_WRITE_BIT, data_size, |
| 0 /* data */); |
| buffer.Bind(); |
| |
| /* |
| * - execute tested operation, to update whole buffer with incrementing values |
| * starting from 0; No error should be generated; |
| */ |
| gl.bufferSubData(GL_ARRAY_BUFFER, 0 /* offset */, data_size, incrementing_data); |
| GLenum error = gl.getError(); |
| |
| if (GL_NO_ERROR != error) |
| { |
| test_result = false; |
| |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << "BufferSubData generated unexpected error: " |
| << glu::getErrorStr(error).toString().c_str() << tcu::TestLog::EndMessage; |
| } |
| |
| if (0 != gl.namedBufferSubData) |
| { |
| gl.namedBufferSubData(buffer.m_id, 0 /* offset */, data_size, incrementing_data); |
| error = gl.getError(); |
| } |
| |
| gl.namedBufferSubData(buffer.m_id, 0 /* offset */, data_size, incrementing_data); |
| error = gl.getError(); |
| |
| if (GL_NO_ERROR != error) |
| { |
| test_result = false; |
| |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message |
| << "NamedBufferSubData generated unexpected error: " << glu::getErrorStr(error).toString().c_str() |
| << tcu::TestLog::EndMessage; |
| } |
| |
| /* |
| * - map buffer contents with MapBufferRange; <access> should contain |
| * MAP_PERSISTENT_BIT, MAP_READ_BIT and MAP_WRITE_BIT; Provide 16 as <offset> |
| * and <size>; |
| * - mapped region should contain values from 16 to 31; |
| * - execute tested operation, to update portions of buffer specified below; |
| * No error should be generated; |
| */ |
| { |
| const Buffer::MapOwner map(buffer.MapRange(mapped_region_offset, mapped_region_size, |
| GL_MAP_PERSISTENT_BIT | GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)); |
| |
| if (0 != memcmp(map.m_data, incrementing_data + mapped_region_offset, mapped_region_size)) |
| { |
| test_result = false; |
| |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << "Mapped region contains unexpected data" |
| << tcu::TestLog::EndMessage; |
| } |
| |
| for (size_t i = 0; i < n_test_cases; ++i) |
| { |
| const GLintptr offset = test_cases[i].m_offset; |
| const GLsizeiptr size = test_cases[i].m_size; |
| |
| gl.bufferSubData(GL_ARRAY_BUFFER, offset, size, incrementing_data); |
| error = gl.getError(); |
| |
| if (GL_NO_ERROR != error) |
| { |
| test_result = false; |
| |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message |
| << "BufferSubData generated unexpected error: " << glu::getErrorStr(error).toString().c_str() |
| << tcu::TestLog::EndMessage; |
| } |
| |
| if (0 != gl.namedBufferSubData) |
| { |
| gl.namedBufferSubData(buffer.m_id, offset, size, incrementing_data); |
| error = gl.getError(); |
| } |
| |
| if (GL_NO_ERROR != error) |
| { |
| test_result = false; |
| |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message |
| << "NamedBufferSubData generated unexpected error: " << glu::getErrorStr(error).toString().c_str() |
| << tcu::TestLog::EndMessage; |
| } |
| } |
| } |
| |
| /* |
| * - unmap buffer; |
| * - map buffer contents again, this time do not provide MAP_PERSISTENT_BIT; |
| * - execute tested operation to update regions specified below; It is expected |
| * that INVALID_OPERATION will be generated for cases that cross mapped region; |
| * No error should be generated for other cases. |
| */ |
| { |
| Buffer::MapOwner tmp( |
| buffer.MapRange(mapped_region_offset, mapped_region_size, GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)); |
| |
| for (size_t i = 0; i < n_test_cases; ++i) |
| { |
| const GLintptr offset = test_cases[i].m_offset; |
| const GLsizeiptr size = test_cases[i].m_size; |
| const bool is_error_expected = test_cases[i].m_cross_mapped_region; |
| const GLenum expected_error = (true == is_error_expected) ? GL_INVALID_OPERATION : GL_NO_ERROR; |
| |
| gl.bufferSubData(GL_ARRAY_BUFFER, offset, size, incrementing_data); |
| error = gl.getError(); |
| |
| if (expected_error != error) |
| { |
| test_result = false; |
| |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message |
| << "BufferSubData generated wrong error: " << glu::getErrorStr(error).toString().c_str() |
| << ", expected: " << glu::getErrorStr(expected_error).toString().c_str() |
| << ". Mapped region: offset: " << mapped_region_offset << ", size: " << mapped_region_size |
| << ". Operation region: offset: " << offset << ", size: " << size << tcu::TestLog::EndMessage; |
| } |
| |
| if (0 != gl.namedBufferSubData) |
| { |
| gl.namedBufferSubData(buffer.m_id, offset, size, incrementing_data); |
| error = gl.getError(); |
| } |
| |
| if (expected_error != error) |
| { |
| test_result = false; |
| |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message |
| << "NamedBufferSubData generated wrong error: " << glu::getErrorStr(error).toString().c_str() |
| << ", expected: " << glu::getErrorStr(expected_error).toString().c_str() |
| << ". Mapped region: offset: " << mapped_region_offset << ", size: " << mapped_region_size |
| << ". Operation region: offset: " << offset << ", size: " << size << tcu::TestLog::EndMessage; |
| } |
| } |
| } |
| |
| /* Set result */ |
| if (true == test_result) |
| { |
| m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| } |
| else |
| { |
| m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Fail"); |
| } |
| |
| /* Done */ |
| return tcu::TestNode::STOP; |
| } |
| |
| /** Constructor |
| * |
| * @param context Test context |
| **/ |
| MapPersistentTextureTest::MapPersistentTextureTest(deqp::Context& context) |
| : TestCase(context, "map_persistent_texture", "Test texture operations against mapped buffer") |
| , m_compressed_image_size(0) |
| , m_compressed_internal_format(0) |
| { |
| /* Nothing to be done here */ |
| } |
| |
| /** Execute test |
| * |
| * @return tcu::TestNode::STOP otherwise |
| **/ |
| tcu::TestNode::IterateResult MapPersistentTextureTest::iterate() |
| { |
| static const size_t data_size = 256; |
| |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| bool test_result = true; |
| |
| /* Storage for data */ |
| GLubyte data[data_size]; |
| |
| /* Prepare data */ |
| for (size_t i = 0; i < data_size; ++i) |
| { |
| data[i] = (glw::GLubyte)i; |
| } |
| |
| /* Load DSA */ |
| Buffer::LoadExtDirectStateAccess(m_context); |
| Texture::LoadExtDirectStateAccess(m_context); |
| |
| /* Get info about compressed image */ |
| getCompressedInfo(); |
| |
| /* Prepare buffer */ |
| Buffer buffer(m_context); |
| buffer.InitStorage(GL_PIXEL_UNPACK_BUFFER, GL_MAP_PERSISTENT_BIT | GL_MAP_READ_BIT | GL_MAP_WRITE_BIT, data_size, |
| data); |
| Buffer::Bind(gl, 0 /* id */, GL_PIXEL_UNPACK_BUFFER); |
| |
| /* |
| * - prepare texture in a way that is relevant for tested operation; |
| * - execute tested operation, no error should be generated; |
| * - delete texture and prepare next one; |
| */ |
| for (GLuint i = 0; i < TESTED_OPERATION_MAX; ++i) |
| { |
| const TESTED_OPERATION operation = (TESTED_OPERATION)i; |
| |
| bool result = verifyTestedOperation(operation, buffer, GL_NO_ERROR); |
| |
| if (false == result) |
| { |
| test_result = false; |
| |
| m_context.getTestContext().getLog() << tcu::TestLog::Message |
| << "Buffer bound to PIXEL_UNPACK_BUFFER is not mapped" |
| << tcu::TestLog::EndMessage; |
| } |
| } |
| |
| /* |
| * - map buffer contents with MapBufferRange, <access> should contain |
| * MAP_PERSISTENT_BIT, MAP_READ_BIT and MAP_WRITE_BIT; |
| * - execute tested operation, no error should be generated; |
| */ |
| for (GLuint i = 0; i < TESTED_OPERATION_MAX; ++i) |
| { |
| const TESTED_OPERATION operation = (TESTED_OPERATION)i; |
| |
| { |
| Buffer::MapOwner tmp( |
| buffer.MapRange(0 /* offset */, data_size, GL_MAP_PERSISTENT_BIT | GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)); |
| } |
| Buffer::Bind(gl, 0 /* id */, GL_PIXEL_UNPACK_BUFFER); |
| |
| bool result = verifyTestedOperation(operation, buffer, GL_NO_ERROR); |
| |
| if (false == result) |
| { |
| test_result = false; |
| |
| m_context.getTestContext().getLog() << tcu::TestLog::Message |
| << "Buffer bound to PIXEL_UNPACK_BUFFER is persistently mapped" |
| << tcu::TestLog::EndMessage; |
| } |
| } |
| |
| /* Set result */ |
| if (true == test_result) |
| { |
| m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| } |
| else |
| { |
| m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Fail"); |
| } |
| |
| /* Done */ |
| return tcu::TestNode::STOP; |
| } |
| |
| /** Return name of operation |
| * |
| * @param operation Operation which name will be returned |
| * |
| * @return Name of operation or 0 in case of invalid enum |
| **/ |
| const char* MapPersistentTextureTest::getOperationName(TESTED_OPERATION operation) |
| { |
| const char* name = 0; |
| |
| switch (operation) |
| { |
| case OP_COMPRESSED_TEX_IMAGE: |
| name = "CompressedTexImage"; |
| break; |
| case OP_COMPRESSED_TEX_SUB_IMAGE: |
| name = "CompressedTexSubImage"; |
| break; |
| case OP_COMPRESSED_TEXTURE_SUB_IMAGE: |
| name = "CompressedTextureSubImage"; |
| break; |
| case OP_TEX_IMAGE: |
| name = "TexImage"; |
| break; |
| case OP_TEX_SUB_IMAGE: |
| name = "TexSubImage"; |
| break; |
| default: |
| TCU_FAIL("Invalid enum"); |
| } |
| |
| return name; |
| } |
| |
| /** Check format and size of compressed image |
| * |
| **/ |
| void MapPersistentTextureTest::getCompressedInfo() |
| { |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| /* Texture creation */ |
| Texture texture(m_context); |
| Texture::Generate(gl, texture.m_id); |
| Texture::Bind(gl, texture.m_id, GL_TEXTURE_2D); |
| Texture::Image(gl, GL_TEXTURE_2D, 0, GL_COMPRESSED_RED_RGTC1, 8, 8, 1, GL_RED, GL_UNSIGNED_BYTE, |
| 0); // glspec 4.5 pg 216 |
| |
| /* Queries */ |
| Texture::GetLevelParameter(gl, GL_TEXTURE_2D, 0 /* level */, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, |
| &m_compressed_image_size); |
| Texture::GetLevelParameter(gl, GL_TEXTURE_2D, 0 /* level */, GL_TEXTURE_INTERNAL_FORMAT, |
| &m_compressed_internal_format); |
| } |
| |
| /** Verifies results of tested operation |
| * |
| * @param operation Operation to be tested |
| * @param buffer Buffer that will be used as GL_PIXEL_UNPACK_BUFFER |
| * @param expected_error Expected error |
| * |
| * @return false in case of any error, true otherwise |
| **/ |
| bool MapPersistentTextureTest::verifyTestedOperation(TESTED_OPERATION operation, Buffer& buffer, |
| glw::GLenum expected_error) |
| { |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| bool result = true; |
| |
| GLenum error = GL_NO_ERROR; |
| Texture texture(m_context); |
| |
| /* Prepare texture */ |
| Texture::Generate(gl, texture.m_id); |
| Texture::Bind(gl, texture.m_id, GL_TEXTURE_2D); |
| |
| switch (operation) |
| { |
| case OP_COMPRESSED_TEX_IMAGE: |
| case OP_TEX_IMAGE: |
| break; |
| case OP_COMPRESSED_TEX_SUB_IMAGE: |
| case OP_COMPRESSED_TEXTURE_SUB_IMAGE: |
| Texture::CompressedImage(gl, GL_TEXTURE_2D, 0 /* level */, m_compressed_internal_format, 8 /* width */, |
| 8 /* height */, 0 /* depth */, m_compressed_image_size /* imageSize */, |
| 0 /* empty image */); |
| break; |
| case OP_TEX_SUB_IMAGE: |
| Texture::Image(gl, GL_TEXTURE_2D, 0 /* level */, GL_R8, 8 /* width */, 8 /* height */, 0 /* depth */, GL_RED, |
| GL_UNSIGNED_BYTE, 0 /* empty image */); |
| break; |
| default: |
| TCU_FAIL("Invalid enum"); |
| } |
| |
| /* Bind buffer to PIXEL_UNPACK */ |
| Buffer::Bind(gl, buffer.m_id, GL_PIXEL_UNPACK_BUFFER); |
| |
| /* Execute operation */ |
| switch (operation) |
| { |
| case OP_COMPRESSED_TEX_IMAGE: |
| gl.compressedTexImage2D(GL_TEXTURE_2D, 0 /* level */, m_compressed_internal_format, 8 /* width */, |
| 8 /* height */, 0 /* border */, m_compressed_image_size /* imageSize */, |
| 0 /* offset to pixel unpack buffer */); |
| error = gl.getError(); |
| break; |
| case OP_COMPRESSED_TEX_SUB_IMAGE: |
| gl.compressedTexSubImage2D(GL_TEXTURE_2D, 0 /* level */, 0 /* x */, 0 /* y */, 8 /* width */, 8 /* height */, |
| m_compressed_internal_format, m_compressed_image_size, |
| 0 /* offset to pixel unpack buffer */); |
| error = gl.getError(); |
| break; |
| case OP_COMPRESSED_TEXTURE_SUB_IMAGE: |
| if (0 != gl.compressedTextureSubImage2D) |
| { |
| gl.compressedTextureSubImage2D(texture.m_id, 0 /* level */, 0 /* x */, 0 /* y */, 8 /* width */, |
| 8 /* height */, m_compressed_internal_format, m_compressed_image_size, |
| 0 /* offset to pixel unpack buffer */); |
| error = gl.getError(); |
| } |
| else |
| { |
| /* Not supported, ignore */ |
| error = expected_error; |
| } |
| break; |
| case OP_TEX_IMAGE: |
| gl.texImage2D(GL_TEXTURE_2D, 0 /* level */, GL_R8, 8 /* width */, 8 /* height */, 0 /* border */, GL_RED, |
| GL_UNSIGNED_BYTE, 0 /* offset to pixel unpack buffer */); |
| error = gl.getError(); |
| break; |
| case OP_TEX_SUB_IMAGE: |
| gl.texSubImage2D(GL_TEXTURE_2D, 0 /* level */, 0 /* x */, 0 /* y */, 8 /* width */, 8 /* height */, GL_RED, |
| GL_UNSIGNED_BYTE, 0 /* offset to pixel unpack buffer */); |
| error = gl.getError(); |
| break; |
| default: |
| TCU_FAIL("Invalid enum"); |
| } |
| |
| /* Unbind buffer */ |
| Buffer::Bind(gl, 0 /* id */, GL_PIXEL_UNPACK_BUFFER); |
| |
| /* Check result */ |
| if (expected_error != error) |
| { |
| result = false; |
| |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << getOperationName(operation) |
| << " generated wrong error: " << glu::getErrorStr(error).toString().c_str() |
| << ", expected: " << glu::getErrorStr(expected_error).toString().c_str() |
| << tcu::TestLog::EndMessage; |
| } |
| |
| /* Done */ |
| return result; |
| } |
| |
| /** Constructor |
| * |
| * @param context Test context |
| **/ |
| MapPersistentReadPixelsTest::MapPersistentReadPixelsTest(deqp::Context& context) |
| : TestCase(context, "map_persistent_read_pixels", "Test read pixels operation against mapped buffer") |
| { |
| /* Nothing to be done here */ |
| } |
| |
| /** Execute test |
| * |
| * @return tcu::TestNode::STOP otherwise |
| **/ |
| tcu::TestNode::IterateResult MapPersistentReadPixelsTest::iterate() |
| { |
| static const GLuint height = 8; |
| static const GLuint width = 8; |
| static const size_t data_size = width * height; |
| |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| bool test_result = true; |
| |
| /* Prepare data */ |
| GLubyte initial_texture_data[data_size]; |
| GLubyte updated_texture_data[data_size]; |
| |
| for (size_t i = 0; i < data_size; ++i) |
| { |
| initial_texture_data[i] = (glw::GLubyte)i; |
| updated_texture_data[i] = (glw::GLubyte)(data_size - i); |
| } |
| |
| /* Prepare GL objects */ |
| Buffer buffer(m_context); |
| Framebuffer framebuffer(m_context); |
| Texture texture(m_context); |
| |
| buffer.InitStorage(GL_PIXEL_PACK_BUFFER, GL_MAP_PERSISTENT_BIT | GL_MAP_READ_BIT | GL_MAP_WRITE_BIT, data_size, |
| 0 /* data */); |
| |
| Texture::Generate(gl, texture.m_id); |
| Texture::Bind(gl, texture.m_id, GL_TEXTURE_2D); |
| Texture::Storage(gl, GL_TEXTURE_2D, 1 /* levels */, GL_R8UI, width, height, 0 /* depth */); |
| Texture::SubImage(gl, GL_TEXTURE_2D, 0 /* level */, 0 /* x */, 0 /* y */, 0 /* z */, width, height, 0 /* depth */, |
| GL_RED_INTEGER, GL_UNSIGNED_BYTE, initial_texture_data); |
| |
| Framebuffer::Generate(gl, framebuffer.m_id); |
| Framebuffer::Bind(gl, GL_READ_FRAMEBUFFER, framebuffer.m_id); |
| Framebuffer::AttachTexture(gl, GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture.m_id, width, height); |
| |
| /* |
| * - execute ReadPixels to transfer texture contents to buffer, no error should |
| * be generated; |
| */ |
| buffer.Bind(); |
| gl.readPixels(0 /* x */, 0 /* y */, width, height, GL_RED_INTEGER, GL_UNSIGNED_BYTE, |
| 0 /* offset in PIXEL_PACK_BUFFER */); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "ReadPixels to not mapped PIXEL_PACK buffer"); |
| |
| /* |
| * - update contents of texture with different image; |
| * - map buffer contents with MapBufferRange, <access> should contain |
| * MAP_PERSISTENT_BIT, MAP_READ_BIT and MAP_WRITE_BIT; |
| * - execute ReadPixels to transfer texture contents to buffer, no error should |
| * be generated; |
| * - execute MemoryBarrier with CLIENT_MAPPED_BUFFER_BARRIER_BIT and Finish; |
| * - inspect contents of mapped buffer, to verify that latest data transfer was |
| * successful; |
| * - unmap buffer |
| */ |
| { |
| Texture::SubImage(gl, GL_TEXTURE_2D, 0 /* level */, 0 /* x */, 0 /* y */, 0 /* z */, width, height, |
| 0 /* depth */, GL_RED_INTEGER, GL_UNSIGNED_BYTE, updated_texture_data); |
| |
| const Buffer::MapOwner map( |
| buffer.MapRange(0 /* offset */, data_size, GL_MAP_PERSISTENT_BIT | GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)); |
| |
| buffer.Bind(); |
| gl.readPixels(0 /* x */, 0 /* y */, width, height, GL_RED_INTEGER, GL_UNSIGNED_BYTE, |
| 0 /* offset in PIXEL_PACK_BUFFER */); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "ReadPixels to persistently mapped PIXEL_PACK buffer"); |
| |
| gl.memoryBarrier(GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "MemoryBarrier"); |
| |
| gl.finish(); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "Finish"); |
| |
| if (0 != memcmp(updated_texture_data, map.m_data, data_size)) |
| { |
| test_result = false; |
| |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "Wrong contents of persistently mapped PIXEL_PACK buffer after ReadPixels" |
| << tcu::TestLog::EndMessage; |
| } |
| } |
| |
| /* |
| * - map buffer contents again, this time do not provide MAP_PERSISTENT_BIT; |
| * - execute ReadPixels to transfer texture contents to buffer, |
| * INVALID_OPERATION error should be generated. |
| */ |
| { |
| Buffer::MapOwner tmp(buffer.MapRange(0 /* offset */, data_size, GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)); |
| |
| buffer.Bind(); |
| gl.readPixels(0 /* x */, 0 /* y */, width, height, GL_RED_INTEGER, GL_UNSIGNED_BYTE, |
| 0 /* offset in PIXEL_PACK_BUFFER */); |
| GLenum error = gl.getError(); |
| |
| if (GL_INVALID_OPERATION != error) |
| { |
| test_result = false; |
| |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "Wrong error was generated by ReadPixels. Expected INVALID_OPERATION as " |
| "PIXEL_PACK buffer is mapped. Got: " |
| << glu::getErrorStr(error).toString().c_str() << tcu::TestLog::EndMessage; |
| } |
| } |
| |
| /* Set result */ |
| if (true == test_result) |
| { |
| m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| } |
| else |
| { |
| m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Fail"); |
| } |
| |
| /* Done */ |
| return tcu::TestNode::STOP; |
| } |
| |
| /** Constructor |
| * |
| * @param context Test context |
| **/ |
| MapPersistentDispatchTest::MapPersistentDispatchTest(deqp::Context& context) |
| : TestCase(context, "map_persistent_dispatch", "test dispatch operation against mapped buffer") |
| { |
| /* Nothing to be done here */ |
| } |
| |
| /** Constructor |
| * |
| * @param context Test context |
| * @param test_name Test name |
| * @param test_description Test description |
| **/ |
| MapPersistentDispatchTest::MapPersistentDispatchTest(deqp::Context& context, const GLchar* test_name, |
| const GLchar* test_description) |
| : TestCase(context, test_name, test_description) |
| { |
| /* Nothing to be done here */ |
| } |
| |
| /** Execute test |
| * |
| * @return tcu::TestNode::STOP otherwise |
| **/ |
| tcu::TestNode::IterateResult MapPersistentDispatchTest::iterate() |
| { |
| static const GLchar* compute_shader = "#version 430 core\n" |
| "\n" |
| "layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n" |
| "\n" |
| "layout (binding = 0, std430) buffer DestinationData {\n" |
| " uint values[];\n" |
| "} destination;\n" |
| "\n" |
| "layout (binding = 1, std430) buffer SourceData {\n" |
| " uint values[];\n" |
| "} source;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " uint index = gl_GlobalInvocationID.x;\n" |
| " uint sum = 0u;\n" |
| "\n" |
| " for (uint i = 0u; i <= index; ++i)\n" |
| " {\n" |
| " sum += source.values[i];\n" |
| " }\n" |
| "\n" |
| " destination.values[index] = sum;\n" |
| "}\n" |
| "\n"; |
| static const GLuint data_size = 16; |
| static const GLuint destination_binding = 0; |
| static const GLuint source_binding = 1; |
| |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| bool test_result = true; |
| |
| /* Prepare data */ |
| GLuint destination_data[data_size]; |
| GLuint modified_source_data[data_size]; |
| GLuint modified_sum_data[data_size]; |
| GLuint source_data[data_size]; |
| GLuint sum_data[data_size]; |
| |
| GLuint modified_sum = 0; |
| GLuint sum = 0; |
| |
| for (GLuint i = 0; i < data_size; ++i) |
| { |
| destination_data[i] = 0; |
| modified_source_data[i] = data_size - i; |
| source_data[i] = i; |
| |
| modified_sum += modified_source_data[i]; |
| sum += source_data[i]; |
| |
| modified_sum_data[i] = modified_sum; |
| sum_data[i] = sum; |
| } |
| |
| /* Prepare buffers */ |
| Buffer destination(m_context); |
| Buffer source(m_context); |
| |
| destination.InitStorage(GL_SHADER_STORAGE_BUFFER, GL_MAP_COHERENT_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_READ_BIT, |
| data_size * sizeof(GLuint), destination_data); |
| |
| source.InitStorage(GL_SHADER_STORAGE_BUFFER, GL_MAP_COHERENT_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_WRITE_BIT, |
| data_size * sizeof(GLuint), source_data); |
| |
| /* Prepare program */ |
| Program program(m_context); |
| program.Init(compute_shader, "" /* fs */, "" /* gs */, "" /* tcs */, "" /* tes */, "" /* vs */); |
| |
| /* |
| * - bind buffers to SHADER_STORAGE_BUFFER; |
| * - use MapBufferRange to map both buffers; <access> shall be set as follows: |
| * * MAP_COHERENT_BIT and MAP_PERSISTENT_BIT flags set for both |
| * * MAP_WRITE_BIT flag shall be set for source; |
| * * MAP_READ_BIT flag shall be set for destination; |
| * - dispatch program for 16x1x1 groups; |
| * - modify contents of source buffer via mapped memory; |
| * - execute Finish; |
| * - inspect contents of destination buffer via mapped memory; It is expected |
| * that it will contain results based on original content of source buffer; |
| * - dispatch program for 16x1x1 groups; |
| * - execute Finish; |
| * - inspect contents of destination buffer via mapped memory; It is expected |
| * that it will contain results based on modified content of source buffer. |
| */ |
| { |
| /* Set program */ |
| Program::Use(gl, program.m_id); |
| |
| /* Map buffers */ |
| destination.Bind(); |
| const Buffer::MapOwner destination_map(destination.MapRange( |
| 0 /* offset */, data_size * sizeof(GLuint), GL_MAP_COHERENT_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_READ_BIT)); |
| |
| source.Bind(); |
| const Buffer::MapOwner source_map( |
| source.MapRange(0 /* offset */, data_size * sizeof(GLuint), |
| GL_MAP_COHERENT_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_WRITE_BIT)); |
| |
| /* Clear binding point */ |
| Buffer::Bind(gl, 0, GL_SHADER_STORAGE_BUFFER); |
| |
| /* Bind buffers */ |
| Buffer::BindBase(gl, destination.m_id, GL_SHADER_STORAGE_BUFFER, destination_binding); |
| Buffer::BindBase(gl, source.m_id, GL_SHADER_STORAGE_BUFFER, source_binding); |
| |
| /* Execute program for 16x1x1 groups */ |
| gl.dispatchCompute(16, 1, 1); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "DispatchCompute with persistently mapped buffers"); |
| |
| /* Make sure that program executed */ |
| gl.finish(); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "Finish"); |
| |
| if (0 != memcmp(destination_map.m_data, sum_data, data_size * sizeof(GLuint))) |
| { |
| test_result = false; |
| |
| m_context.getTestContext().getLog() << tcu::TestLog::Message |
| << "Contents of mapped region does not correspond with expected results" |
| << tcu::TestLog::EndMessage; |
| } |
| |
| /* Modify source buffer via mapped area */ |
| memcpy(source_map.m_data, modified_source_data, data_size * sizeof(GLuint)); |
| |
| /* Execute program for 16x1x1 groups */ |
| gl.dispatchCompute(16, 1, 1); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "DispatchCompute with persistently mapped buffers"); |
| |
| /* Make s
|