413 lines
15 KiB
C++
Executable File
413 lines
15 KiB
C++
Executable File
// =====================================================================================================================
|
|
// 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 <https://www.gnu.org/licenses/>.
|
|
// =====================================================================================================================
|
|
|
|
#ifndef GLW_BUFFER_H
|
|
#define GLW_BUFFER_H
|
|
|
|
#include "common.h"
|
|
|
|
#include <glm/common.hpp>
|
|
#include <open-cpp-utils/dynarray.h>
|
|
|
|
namespace ocu = open_cpp_utils;
|
|
|
|
namespace glw
|
|
{
|
|
|
|
// =====================================================================================================================
|
|
// Definitions
|
|
// =====================================================================================================================
|
|
|
|
|
|
// Typedefs ------------------------------------------------------------------------------------------------------------
|
|
|
|
using buffer_t = GLenum;
|
|
|
|
template<buffer_t T, flags_t Fs>
|
|
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<flags_t Fs> using vertex_buffer = buffer<buf_vertex, Fs>;
|
|
template<flags_t Fs> using element_buffer = buffer<buf_element, Fs>;
|
|
template<flags_t Fs> using copy_read_buffer = buffer<buf_copy_read, Fs>;
|
|
template<flags_t Fs> using copy_write_buffer = buffer<buf_copy_write, Fs>;
|
|
template<flags_t Fs> using pixel_pack_buffer = buffer<buf_pixel_pack, Fs>;
|
|
template<flags_t Fs> using pixel_unpack_buffer = buffer<buf_pixel_unpack, Fs>;
|
|
template<flags_t Fs> using query_buffer = buffer<buf_query, Fs>;
|
|
template<flags_t Fs> using texture_buffer = buffer<buf_texture, Fs>;
|
|
template<flags_t Fs> using transform_feedback_buffer = buffer<buf_transform_feedback, Fs>;
|
|
template<flags_t Fs> using uniform_buffer = buffer<buf_uniform, Fs>;
|
|
template<flags_t Fs> using indirect_draw_buffer = buffer<buf_indirect_draw, Fs>;
|
|
template<flags_t Fs> using atomic_counter_buffer = buffer<buf_atomic_counter, Fs>;
|
|
template<flags_t Fs> using indirect_dispatch_buffer = buffer<buf_indirect_dispatch, Fs>;
|
|
template<flags_t Fs> using shader_storage_buffer = buffer<buf_shader_storage, Fs>;
|
|
template<flags_t Fs> using parameter_buffer = buffer<buf_parameter, Fs>;
|
|
|
|
|
|
// Buffer Definition ---------------------------------------------------------------------------------------------------
|
|
|
|
template<buffer_t T_, flags_t Fs_>
|
|
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<typename T>
|
|
T* data() { static_assert(flags | buffer_flags::write); return map_; }
|
|
|
|
/**
|
|
* \brief
|
|
* \tparam T
|
|
* \return
|
|
*/
|
|
template<typename T>
|
|
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<typename T>
|
|
[[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<enum_t oType, flags_t oFlags>
|
|
void copy(const buffer<oType, oFlags>& 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_t Type, flags_t Flags>
|
|
buffer<Type, Flags>::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_t Type, flags_t Flags>
|
|
buffer<Type, Flags>::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_t Type, flags_t Flags>
|
|
buffer<Type, Flags>::~buffer()
|
|
{
|
|
glDeleteBuffers(1, &handle_);
|
|
}
|
|
|
|
template<buffer_t Type, flags_t Flags>
|
|
bool buffer<Type, Flags>::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<buffer_t Type, flags_t Flags>
|
|
void buffer<Type, Flags>::unmap()
|
|
{
|
|
glUnmapNamedBuffer(handle_);
|
|
map_ = nullptr;
|
|
}
|
|
|
|
template<buffer_t Type, flags_t Flags>
|
|
void buffer<Type, Flags>::bind()
|
|
{
|
|
glBindBuffer(type, handle_);
|
|
}
|
|
|
|
template<buffer_t Type, flags_t Flags>
|
|
void buffer<Type, Flags>::attach(location_t location)
|
|
{
|
|
glBindBufferBase(type, location, handle_);
|
|
}
|
|
|
|
template<buffer_t Type, flags_t Flags>
|
|
void buffer<Type, Flags>::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<buffer_t Type, flags_t Flags>
|
|
void buffer<Type, Flags>::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<buffer_t Type, flags_t Flags>
|
|
void buffer<Type, Flags>::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<buffer_t Type, flags_t Flags>
|
|
template<buffer_t oType, flags_t oFlags>
|
|
void buffer<Type, Flags>::copy(const buffer<oType, oFlags>& src, size_t size,
|
|
offset_t src_offset, offset_t dst_offset)
|
|
{
|
|
glCopyNamedBufferSubData(src.handle_, handle_, src_offset, dst_offset, size);
|
|
}
|
|
|
|
template<buffer_t Type, flags_t Flags>
|
|
void buffer<Type, Flags>::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
|