251 lines
9.4 KiB
C++
251 lines
9.4 KiB
C++
// =====================================================================================================================
|
|
// fennec, a free and open source game engine
|
|
// Copyright © 2025 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 FENNEC_RENDERERS_OPENGL_LIB_BUFFER_H
|
|
#define FENNEC_RENDERERS_OPENGL_LIB_BUFFER_H
|
|
|
|
#include <fennec/math/common.h>
|
|
#include <fennec/renderers/opengl/lib/fwd.h>
|
|
#include <fennec/renderers/opengl/lib/enum.h>
|
|
|
|
namespace fennec
|
|
{
|
|
|
|
namespace gl
|
|
{
|
|
|
|
template<GLbitfield FlagsV, GLboolean ImmutableV> using vertex_buffer = buffer<VERTEX, FlagsV, ImmutableV>;
|
|
template<GLbitfield FlagsV, GLboolean ImmutableV> using element_buffer = buffer<ELEMENT, FlagsV, ImmutableV>;
|
|
template<GLbitfield FlagsV, GLboolean ImmutableV> using uniform_buffer = buffer<UNIFORM, FlagsV, ImmutableV>;
|
|
template<GLbitfield FlagsV, GLboolean ImmutableV> using shader_storage_buffer = buffer<SHADER_STORAGE, FlagsV, ImmutableV>;
|
|
template<GLbitfield FlagsV, GLboolean ImmutableV> using query_buffer = buffer<QUERY, FlagsV, ImmutableV>;
|
|
template<GLbitfield FlagsV, GLboolean ImmutableV> using texture_buffer = buffer<TEXTURE, FlagsV, ImmutableV>;
|
|
template<GLbitfield FlagsV, GLboolean ImmutableV> using transform_feedback_buffer = buffer<TRANSFORM_FEEDBACK, FlagsV, ImmutableV>;
|
|
template<GLbitfield FlagsV, GLboolean ImmutableV> using atomic_counter_buffer = buffer<ATOMIC_COUNTER, FlagsV, ImmutableV>;
|
|
template<GLbitfield FlagsV, GLboolean ImmutableV> using parameter_buffer = buffer<PARAMETER, FlagsV, ImmutableV>;
|
|
template<GLbitfield FlagsV, GLboolean ImmutableV> using indirect_draw_buffer = buffer<INDIRECT_DRAW, FlagsV, ImmutableV>;
|
|
template<GLbitfield FlagsV, GLboolean ImmutableV> using indirect_dispatch_buffer = buffer<INDIRECT_DISPATCH, FlagsV, ImmutableV>;
|
|
template<GLbitfield FlagsV, GLboolean ImmutableV> using copy_read_buffer = buffer<COPY_READ, FlagsV, ImmutableV>;
|
|
template<GLbitfield FlagsV, GLboolean ImmutableV> using copy_write_buffer = buffer<COPY_WRITE, FlagsV, ImmutableV>;
|
|
template<GLbitfield FlagsV, GLboolean ImmutableV> using pixel_pack_buffer = buffer<PIXEL_PACK, FlagsV, ImmutableV>;
|
|
template<GLbitfield FlagsV, GLboolean ImmutableV> using pixel_unpack_buffer = buffer<PIXEL_UNPACK, FlagsV, ImmutableV>;
|
|
|
|
template<GLenum TypeV, GLbitfield FlagsV, GLboolean ImmutableV>
|
|
class buffer {
|
|
|
|
// HELPER FUNCTIONS ====================================================================================================
|
|
|
|
private:
|
|
static constexpr GLenum get_mutable_traits() {
|
|
GLenum res;
|
|
|
|
// Set READ/DRAW/COPY
|
|
if constexpr (read) {
|
|
res = GL_STREAM_READ;
|
|
} else if constexpr (write) {
|
|
res = GL_STREAM_DRAW;
|
|
} else {
|
|
res = GL_STREAM_COPY;
|
|
}
|
|
|
|
// Set STATIC/DYNAMIC/STREAM
|
|
if constexpr (client or coherent) {
|
|
// do nothing
|
|
} else if constexpr (dynamic or persistent) {
|
|
res += 6;
|
|
} else {
|
|
res += 3;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
// CONSTANTS ===========================================================================================================
|
|
|
|
public:
|
|
static constexpr GLenum type = TypeV;
|
|
static constexpr GLboolean immutable = ImmutableV;
|
|
static constexpr GLbitfield flags = FlagsV;
|
|
static constexpr GLboolean indexed = type == ATOMIC_COUNTER or type == SHADER_STORAGE or type == TRANSFORM_FEEDBACK or type == UNIFORM;
|
|
static constexpr GLboolean map_read = flags & READ;
|
|
static constexpr GLboolean map_write = flags & WRITE;
|
|
static constexpr GLboolean mapped = map_read or map_write;
|
|
static constexpr GLboolean dynamic = flags & DYNAMIC;
|
|
static constexpr GLboolean persistent = flags & PERSISTENT;
|
|
static constexpr GLboolean coherent = flags & COHERENT;
|
|
static constexpr GLboolean client = flags & CLIENT;
|
|
static constexpr GLenum usage = get_mutable_traits();
|
|
|
|
static_assert(not persistent or persistent == mapped);
|
|
static_assert(not coherent or coherent == persistent);
|
|
|
|
|
|
// CONSTRUCTORS & ASSIGNMENT ===========================================================================================
|
|
|
|
constexpr buffer(const void* data, GLsizeiptr size)
|
|
: _handle()
|
|
, _size(size) {
|
|
glGenBuffers(1, &_handle);
|
|
start();
|
|
if constexpr(immutable) {
|
|
glBufferStorage(type, _size, data, flags);
|
|
} else {
|
|
glBufferData(type, _size, data, usage);
|
|
}
|
|
}
|
|
|
|
constexpr buffer(buffer&& buff) noexcept
|
|
: _handle(buff)
|
|
, _size(buff._size)
|
|
, _mapping(nullptr) {
|
|
}
|
|
|
|
constexpr buffer& operator=(buffer&& buff) noexcept {
|
|
glDeleteBuffers(1, &_handle);
|
|
_handle = buff._handle;
|
|
_size = buff._size;
|
|
return *this;
|
|
}
|
|
|
|
constexpr buffer(const buffer&) = delete;
|
|
constexpr buffer& operator=(const buffer&) = delete;
|
|
|
|
|
|
// OBJECT FUNCTIONS ====================================================================================================
|
|
|
|
constexpr void start() const {
|
|
glBindBuffer(type, _handle);
|
|
}
|
|
|
|
constexpr void end() const {
|
|
glBindBuffer(type, NULL);
|
|
}
|
|
|
|
constexpr void bind(GLuint i, GLsizeiptr size = -1, GLintptr offset = 0) {
|
|
offset = max(offset, GLintptr(0));
|
|
size = size < 0 ? _size : size;
|
|
size = min(size, _size - offset);
|
|
if (size <= 0) return;
|
|
glBindBufferRange(type, i, _handle, offset, size);
|
|
}
|
|
|
|
|
|
// BASIC BUFFER FUNCTIONS ==============================================================================================
|
|
|
|
constexpr void resize(GLsizei size) requires not immutable {
|
|
unmap();
|
|
glBufferData(type, _size = size, nullptr, usage);
|
|
}
|
|
|
|
constexpr void* map(GLbitfield access, GLsizeiptr size, GLintptr offset = 0) {
|
|
if (_mapping) {
|
|
return _mapping;
|
|
}
|
|
offset = max(offset, GLintptr(0));
|
|
size = size < 0 ? _size : size;
|
|
size = min(size, _size - offset);
|
|
if (size <= 0) return nullptr;
|
|
return _mapping = glMapBufferRange(type, offset, size, _mapflags = access);
|
|
}
|
|
|
|
constexpr void unmap() {
|
|
glUnmapBuffer(type);
|
|
_mapping = nullptr;
|
|
}
|
|
|
|
constexpr void clear(GLsizeiptr size = -1, GLintptr offset = 0, const void* data = nullptr, GLenum data_type = BYTE, GLenum format = R, GLenum internal = R8) {
|
|
offset = max(offset, GLintptr(0));
|
|
size = size < 0 ? _size : size;
|
|
size = min(size, _size - offset);
|
|
if (size <= 0) return;
|
|
glClearBufferSubData(type, internal, offset, size, format, type, data);
|
|
}
|
|
|
|
template<GLenum OTypeV, GLbitfield OFlagsV, GLboolean OImmutableV>
|
|
constexpr void copy(const buffer<OTypeV, OFlagsV, OImmutableV>& cpy, GLsizeiptr size = -1, GLintptr write_offset = 0, GLintptr read_offset = 0) {
|
|
write_offset = max(write_offset, GLintptr(0));
|
|
read_offset = max(read_offset, GLintptr(0));
|
|
size = size < 0 ? _size : size;
|
|
size = fennec::min(size, cpy._size - read_offset);
|
|
size = fennec::min(size, _size - write_offset);
|
|
if (size <= 0) return;
|
|
glBindBuffer(COPY_READ, cpy._handle);
|
|
glBindBuffer(COPY_WRITE, _handle);
|
|
glCopyBufferSubData(COPY_READ, COPY_WRITE, read_offset, write_offset, size);
|
|
glBindBuffer(COPY_READ, NULL);
|
|
glBindBuffer(COPY_WRITE, NULL);
|
|
}
|
|
|
|
constexpr void read(void* data, GLsizeiptr size, GLintptr offset = 0) const {
|
|
static_assert(dynamic or persistent and not (client or coherent));
|
|
offset = max(offset, GLintptr(0));
|
|
size = size < 0 ? _size : size;
|
|
size = min(size, _size - offset);
|
|
if (size <= 0) return;
|
|
glGetBufferSubData(type, offset, size, data);
|
|
}
|
|
|
|
constexpr void write(const void* data, GLsizeiptr size, GLintptr offset = 0) {
|
|
static_assert(dynamic or persistent and not (client or coherent));
|
|
offset = max(offset, GLintptr(0));
|
|
size = size < 0 ? _size : size;
|
|
size = min(size, _size - offset);
|
|
if (size <= 0) return;
|
|
glBufferSubData(type, offset, size, data);
|
|
}
|
|
|
|
constexpr void flush(GLsizeiptr size = -1, GLintptr offset = 0) {
|
|
if (not _mapping) return;
|
|
if (not (_mapflags & EXPLICIT_FLUSH)) return;
|
|
offset = max(offset, GLintptr(0));
|
|
size = size < 0 ? _size : size;
|
|
size = min(size, _size - offset);
|
|
if (size <= 0) return;
|
|
glFlushMappedBufferRange(type, offset, size);
|
|
}
|
|
|
|
constexpr void invalidate(GLsizeiptr size, GLintptr offset = 0) {
|
|
offset = max(offset, GLintptr(0));
|
|
size = size < 0 ? _size : size;
|
|
size = min(size, _size - offset);
|
|
if (size <= 0) return;
|
|
glInvalidateBufferSubData(type, offset, size);
|
|
}
|
|
|
|
|
|
// TYPED FUNCTIONS =====================================================================================================
|
|
|
|
template<typename TypeT>
|
|
constexpr TypeT* map(GLbitfield access, GLsizeiptr n, GLintptr offset = 0) {
|
|
return static_cast<TypeT*>(map(access, n * sizeof(TypeT), offset * sizeof(TypeT)));
|
|
}
|
|
|
|
private:
|
|
GLuint _handle;
|
|
GLsizeiptr _size;
|
|
void* _mapping;
|
|
GLbitfield _mapflags;
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif // FENNEC_PLATFORM_OPENGL_LIB_H
|