// =====================================================================================================================
// glw, an open-source library that wraps OpenGL structures into classes.
// Copyright (C) 2024 Medusa Slockbower
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
// =====================================================================================================================
#ifndef GLW_BUFFER_H
#define GLW_BUFFER_H
#include "common.h"
#include
#include
namespace ocu = open_cpp_utils;
namespace glw
{
// =====================================================================================================================
// Definitions
// =====================================================================================================================
// Typedefs ------------------------------------------------------------------------------------------------------------
using buffer_t = GLenum;
template
class buffer;
// Enums ---------------------------------------------------------------------------------------------------------------
enum buffer_type
{
buf_vertex = GL_ARRAY_BUFFER
, buf_element = GL_ELEMENT_ARRAY_BUFFER
, buf_copy_read = GL_COPY_READ_BUFFER
, buf_copy_write = GL_COPY_WRITE_BUFFER
, buf_pixel_pack = GL_PIXEL_PACK_BUFFER
, buf_pixel_unpack = GL_PIXEL_UNPACK_BUFFER
, buf_query = GL_QUERY_BUFFER
, buf_texture = GL_TEXTURE_BUFFER
, buf_transform_feedback = GL_TRANSFORM_FEEDBACK_BUFFER
, buf_uniform = GL_UNIFORM_BUFFER
, buf_indirect_draw = GL_DRAW_INDIRECT_BUFFER
, buf_atomic_counter = GL_ATOMIC_COUNTER_BUFFER
, buf_indirect_dispatch = GL_DISPATCH_INDIRECT_BUFFER
, buf_shader_storage = GL_SHADER_STORAGE_BUFFER
, buf_parameter = GL_PARAMETER_BUFFER
};
enum buffer_flags
{
buf_flags_read = GL_MAP_READ_BIT
, buf_flags_write = GL_MAP_WRITE_BIT
, buf_flags_read_write = buf_flags_read | buf_flags_write
, buf_flags_dynamic = GL_DYNAMIC_STORAGE_BIT
, buf_flags_persistent = GL_MAP_PERSISTENT_BIT
, buf_flags_coherent = GL_MAP_COHERENT_BIT | bf_persistent
, buf_flags_client = GL_CLIENT_STORAGE_BIT
};
enum mapping_flags
{
buf_map_read = GL_MAP_READ_BIT
, buf_map_write = GL_MAP_WRITE_BIT
, buf_map_read_write = buf_map_read | buf_map_write
, buf_map_persistent = GL_MAP_PERSISTENT_BIT
, buf_map_coherent = GL_MAP_COHERENT_BIT | buf_map_persistent
, buf_map_invalidate_range = GL_MAP_INVALIDATE_RANGE_BIT
, buf_map_invalidate_buffer = GL_MAP_INVALIDATE_BUFFER_BIT
, buf_map_flush_explicity = GL_MAP_FLUSH_EXPLICIT_BIT | buf_map_write
, buf_map_unsynchronized = GL_MAP_UNSYNCHRONIZED_BIT
, buf_map_optional = buf_map_invalidate_range
| buf_map_invalidate_buffer
| buf_map_flush_explicity
| buf_map_unsynchronized
};
// Buffer Aliases
template using vertex_buffer = buffer;
template using element_buffer = buffer;
template using copy_read_buffer = buffer;
template using copy_write_buffer = buffer;
template using pixel_pack_buffer = buffer;
template using pixel_unpack_buffer = buffer;
template using query_buffer = buffer;
template using texture_buffer = buffer;
template using transform_feedback_buffer = buffer;
template using uniform_buffer = buffer;
template using indirect_draw_buffer = buffer;
template using atomic_counter_buffer = buffer;
template using indirect_dispatch_buffer = buffer;
template using shader_storage_buffer = buffer;
template using parameter_buffer = buffer;
// Buffer Definition ---------------------------------------------------------------------------------------------------
template
class buffer
{
// Constants ===========================================================================================================
public:
static constexpr buffer_t type = T_;
static constexpr flags_t flags = Fs_;
// Functions ===========================================================================================================
public:
// Constructors --------------------------------------------------------------------------------------------------------
/**
* \brief Construct a buffer filled with the provided data
* \param size Size of the buffer in bytes
* \param data Pointer to the data to initialize the buffer with, if null, initialized with 0
*/
buffer(size_t size, const void* data = nullptr);
buffer(const buffer&);
buffer(buffer&&) = default;
~buffer();
// Access --------------------------------------------------------------------------------------------------------------
/**
* \brief Map the buffer to the CPU
* \param size Size of the region to map
* \param offset Offset into the buffer to map
* \param map_flags Flags to map the buffer with
* \return Whether the buffer was successfully mapped
*/
bool map(size_t size = -1, offset_t offset = 0, flags_t map_flags = mapping_flags::read);
void unmap();
template
T* data() { static_assert(flags | buffer_flags::write); return map_; }
/**
* \brief
* \tparam T
* \return
*/
template
const T* data() const { static_assert(flags | buffer_flags::read); return map_; }
/**
* \brief Bind the buffer for usage with gl functions
*/
void bind();
/**
* \brief Attach the buffer to a specific base location for usage in a shader program
* \param location Location to bind the buffer to
*/
void attach(location_t location);
// Status --------------------------------------------------------------------------------------------------------------
[[nodiscard]] bool mapped() const { return map_ != nullptr; }
[[nodiscard]] bool valid() const { return handle_ != NULL; }
// Capacity ------------------------------------------------------------------------------------------------------------
[[nodiscard]] size_t size() const { return size_; }
template
[[nodiscard]] size_t capacity() const { return size_ / sizeof(T); }
// Modifiers -----------------------------------------------------------------------------------------------------------
/**
* \brief Read a section of the buffer from the gpu into a location on the cpu
* \param buffer Pointer to the buffer to read into
* \param size Size of the buffer
* \param offset Offset into the gpu memory
*/
void read(void* buffer, size_t size, offset_t offset = 0);
/**
* \brief Write a section to the buffer on the gpu from the cpu
* \param buffer Pointer to the buffer to write from
* \param size Size of the buffer
* \param offset Offset into the gpu memory
*/
void write(const void* buffer, size_t size, offset_t offset = 0);
/**
* \brief Clear a portion of the buffer
* \param size Size of the section to clear, if negative, size will be automatically calculated using the offset.
* \param offset Offset into the buffer
* \param data Value to clear the buffer with
* \param layout Layout of the provided data
* \param type Type of the provided data
* \param format Format of the provided data
*/
void clear(size_t size = -1, offset_t offset = 0, const void* data = nullptr,
enum_t layout = r_i, enum_t type = uint8, enum_t format = r8_ui);
/**
* \brief Copy data from another buffer
* \tparam oType Type of the other buffer
* \tparam oFlags Flags of the other buffer
* \param src Source buffer to copy from
* \param size Size of the section of data to copy
* \param src_offset Offset into the source buffer
* \param dst_offset Offset into the destination buffer
*/
template
void copy(const buffer& src, size_t size = -1, offset_t src_offset = 0, offset_t dst_offset = 0);
/**
* \brief Resize the buffer
* \param size New size of the data in bytes
*/
void resize(size_t size);
// Variables ===========================================================================================================
private:
handle_t handle_;
size_t size_;
void* map_;
offset_t map_start_, map_end_;
flags_t map_flags_;
};
// =====================================================================================================================
// Implementations
// =====================================================================================================================
// Constructors --------------------------------------------------------------------------------------------------------
template
buffer::buffer(size_t size, const void *data)
: handle_(NULL)
, size_(size)
, map_(nullptr)
, map_start_(0), map_end_(0)
, map_flags_(0)
{
glCreateBuffers(1, &handle_);
glNamedBufferStorage(handle_, size_, data, flags);
}
template
buffer::buffer(const buffer& other)
: handle_(NULL)
, size_(other.size_)
, map_(nullptr)
, map_start_(0), map_end_(0)
, map_flags_(0)
{
glCreateBuffers(1, &handle_);
glNamedBufferData(handle_, size_, nullptr, flags);
copy(other, size_, 0, 0);
}
// Access --------------------------------------------------------------------------------------------------------------
template
buffer::~buffer()
{
glDeleteBuffers(1, &handle_);
}
template
bool buffer::map(size_t size, offset_t offset, flags_t map_flags)
{
static_assert(flags & read_write);
if(map_) return false;
map_flags &= flags | optional;
if(size < 0) size = size_ - offset;
map_flags_ = map_flags;
map_start_ = offset; map_end_ = map_start_ + size;
return map_ = glMapNamedBufferRange(handle_, offset, size, map_flags_);
}
template
void buffer::unmap()
{
glUnmapNamedBuffer(handle_);
map_ = nullptr;
}
template
void buffer::bind()
{
glBindBuffer(type, handle_);
}
template
void buffer::attach(location_t location)
{
glBindBufferBase(type, location, handle_);
}
template
void buffer::read(void* buffer, size_t size, offset_t offset)
{
// If not mapped, use glGetBufferSubData to avoid mapping the buffer
if(map_ == nullptr)
{
glGetNamedBufferSubData(handle_, offset, size, buffer);
return;
}
// Check if we need to remap the buffer
offset_t start = offset, end = offset + size;
if(start < map_start_ || end > map_end_ || !(map_flags_ & mapping_flags::read))
{
flags_t map_flags = map_flags_ | mapping_flags::read;
start = glm::min(start, map_start_); end = glm::max(end, map_end_);
unmap();
map(end - start, start, map_flags);
}
// Copy from the mapping
offset = offset - map_start_;
memcpy(buffer, map_ + offset, size);
}
template
void buffer::write(const void *buffer, size_t size, offset_t offset)
{
// if the buffer isn't mapped and is dynamic, use glBufferSubData to avoid mapping the buffer
if(map_ == nullptr && flags | dynamic)
{
glNamedBufferSubData(handle_, offset, size, buffer);
}
// Check if we need to remap the buffer
offset_t start = offset, end = offset + size;
if(start < map_start_ || end > map_end_ || !(map_flags_ & mapping_flags::write))
{
flags_t map_flags = map_flags_ | mapping_flags::write;
start = glm::min(start, map_start_); end = glm::max(end, map_end_);
unmap();
map(end - start, start, map_flags);
}
// Copy to the mapping
offset = offset - map_start_;
memcpy(map_ + offset, buffer, size);
}
template
void buffer::clear(size_t size, offset_t offset, const void* data,
layout_t layout, type_t type, format_t format)
{
glClearNamedBufferSubData(handle_, format, offset, size, format, type, data);
}
template
template
void buffer::copy(const buffer& src, size_t size,
offset_t src_offset, offset_t dst_offset)
{
glCopyNamedBufferSubData(src.handle_, handle_, src_offset, dst_offset, size);
}
template
void buffer::resize(size_t size)
{
if(map_) unmap();
handle_t handle;
glCreateBuffers(1, &handle);
glNamedBufferStorage(handle, size, nullptr, flags);
glCopyNamedBufferSubData(handle_, handle, 0, 0, glm::min(size_, size));
glDeleteBuffers(1, &handle_);
handle_ = handle;
size_ = size;
}
}
#endif //BUFFER_H