Large OpenGL Object handling overhaul

This commit is contained in:
NaifBanana
2026-08-20 08:14:28 -05:00
parent bfe020c5c6
commit 3a097df594
17 changed files with 772 additions and 588 deletions
+186 -277
View File
@@ -4,15 +4,17 @@
#include "GLLoad.hpp"
#include <memory>
#include <string>
#include <vector>
#include <NBCore/Errors.hpp>
#include <NBCore/Types.hpp>
#include <NBCore/Logger.hpp>
#include <NBCore/Utils.hpp>
#include "OGLObjects.hpp"
namespace nb {
typedef std::vector<unsigned char> RawVec;
static uint8_t GLSLTypeSize(GLenum type) {
switch(type) {
@@ -35,51 +37,30 @@ static uint8_t GLSLTypeSize(GLenum type) {
}
}
struct VertexAttributePointer {
GLuint buffer = 0;
int32_t offset = 0;
GLsizei stride = -1;
GLuint divisor = 0;
};
struct VertexAttribute {
GLint GLSLSize;
GLenum GLSLType;
GLboolean GLSLNormalization;
VertexAttributePointer ptr;
};
static bool isIndexInVertexAttribute(int i, const VertexAttribute& va) {
if ((i -= va.ptr.offset) < 0) { return false; }
return (i%va.ptr.stride) < GLSLTypeSize(va.GLSLType) * va.GLSLSize;
}
typedef std::vector<VertexAttribute> VertexAttributeList;
template<typename T>
RawVec vectorToRaw(const std::vector<T>& vec) {
ByteVector vectorToRaw(const std::vector<T>& vec) {
unsigned int num_bytes = vec.size() * sizeof(T);
RawVec ret(num_bytes);
ByteVector ret(num_bytes);
memcpy(ret.data(), vec.data(), num_bytes);
return ret;
}
template<typename T, typename S>
RawVec concatVectorsToRaw(const std::vector<T>& vec1, const std::vector<S>& vec2) {
RawVec vec1_raw = vectorToRaw<T>(vec1);
RawVec vec2_raw = vectorToRaw<S>(vec2);
ByteVector concatVectorsToRaw(const std::vector<T>& vec1, const std::vector<S>& vec2) {
ByteVector vec1_raw = vectorToRaw<T>(vec1);
ByteVector vec2_raw = vectorToRaw<S>(vec2);
unsigned int vec1_raw_size = vec1_raw.size();
unsigned int vec2_raw_size = vec2_raw.size();
RawVec ret(vec1_raw_size + vec2_raw_size);
ByteVector ret(vec1_raw_size + vec2_raw_size);
memcpy(ret.data(), vec1_raw.data(), vec1_raw_size);
memcpy(ret.data() + vec1_raw_size, vec2_raw.data(), vec2_raw_size);
return ret;
}
template<typename T>
std::vector<T> rawToVector(const RawVec& vec) {
std::vector<T> rawToVector(const ByteVector& vec) {
if (vec.size() % sizeof(T) != 0) {
throw std::runtime_error("Data size does not align to std::vector<" + std::string(typeid(T).name()) + ">.");
THROW(std::runtime_error("Data size does not align to std::vector<" + std::string(typeid(T).name()) + ">."));
}
unsigned int num_elmts = vec.size() / sizeof(T);
std::vector<T> ret(num_elmts);
@@ -88,278 +69,206 @@ std::vector<T> rawToVector(const RawVec& vec) {
return ret;
}
class BufferError : public std::runtime_error {
public:
const bool error;
BufferError(const std::string& msg, const std::string& file="", int line=-1)
: std::runtime_error(NB::formatDebugString(msg, file, line)), error(true) {}
BufferError(bool isError=true) : std::runtime_error(""), error(isError) {}
};
template <typename ObjectType>
class OpenGLObject {
public:
OpenGLObject(OpenGLObject&&) = default;
OpenGLObject& operator=(OpenGLObject&&) = default;
~OpenGLObject() { remove(); }
class BufferError : public Error<BufferError> {
using Base = Error<BufferError>;
virtual void bind() const = 0;
virtual void unbind() const = 0;
virtual bool isInitialized() const = 0;
GLuint id() const { return _id; }
protected:
OpenGLObject() = default;
OpenGLObject(const OpenGLObject&) = delete;
OpenGLObject& operator=(const OpenGLObject&) = delete;
virtual void remove() const = 0;
GLuint _id;
};
class VAO : public virtual OpenGLObject<VAO> {
public:
using Base = OpenGLObject<VAO>;
public:
using Base::Base;
enum Codes : unsigned int {
UNDEFINED, DATA_OVERFLOW, INVALID_BUFFER, HANDLE_OVERWRITE
};
VAO() { glGenVertexArrays(1, &_id); }
void bind() const { glBindVertexArray(_id); }
void unbind() const { glBindVertexArray(0); }
protected:
using Base::_id;
const
void remove() { glDeleteVertexArrays(1, &_id); }
static const std::string type;
static const ErrorCodeMap ErrorMessages;
};
template <typename BufferType>
class Buffer : public virtual OpenGLObject<Buffer<BufferType>> {
public:
using Base = OpenGLObject<Buffer>;
using Base::Base;
class Buffer : public OpenGLObject {
using Codes = BufferError::Codes;
Buffer(GLenum usage_ = GL_STATIC_DRAW) : usage(usage_) { glGenBuffers(1, &_id); }
public:
using OpenGLObject::OpenGLObject;
Buffer() = default;
Buffer(Buffer&& other) {
*this = std::move(other);
}
Buffer& operator=(Buffer&& rhs) {
if (_id) { THROW(BufferError(Codes::HANDLE_OVERWRITE)); }
_id = rhs._id;
_usage = rhs._usage;
_size = rhs._size;
void bind() const { glBindBuffer(GLTarget, _id); }
void unbind() const { glBindBuffer(GLTarget, 0); }
void data(void* data_, size_t size) const { glBufferData(GLTarget, size, data_, usage); }
void data(nb::ByteVector& data_) const { glBufferData(GLTarget, data_.size(), data_.data(), usage); }
void invalidate() const { glInvalidateBufferData(_id); }
rhs._id = 0;
rhs._usage = GL_STATIC_DRAW;
rhs._size = 0;
return *this;
}
virtual void bind() const override {
if (_id) {
glBindBuffer(Target, _id);
}
THROW(BufferError(Codes::INVALID_BUFFER));
}
virtual void unbind() const override { glBindBuffer(Target, 0); }
virtual GLenum usage() const { return _usage; }
virtual GLenum usage(GLenum usage_) {
_usage = usage_;
data(data(), _usage);
return _usage;
}
virtual size_t size() const { return _size; }
virtual ByteVector data() const {
bind();
ByteVector ret(_size);
glGetBufferSubData(Target, 0, _size, ret.data());
return ret;
}
virtual void data(
const void* data_,
GLsizei size_,
GLenum usage_=GL_STATIC_DRAW
) {
declare();
bind();
glBufferData(Target, size_, data_, usage_);
_size = size_;
_usage = usage_;
}
virtual void data(const ByteVector& data_, GLenum usage_=GL_STATIC_DRAW) {
data(Target, data_.size(), data_.data(), usage_);
const GLenum GLTarget = BufferType::GLTarget;
GLenum usage;
}
virtual void subdata(void* data_, GLsizeiptr size_, GLintptr offset_=0) {
bind();
if (offset_+size_ <= _size) {
THROW(BufferError(BufferError::Codes::DATA_OVERFLOW));
}
glBufferSubData(Target, offset_, size_, data_);
}
virtual void subdata(ByteVector& data_, GLintptr offset_=0) {
bind();
size_t size_ = data_.size();
if (offset_+size_ <= _size) {
THROW(BufferError(BufferError::Codes::DATA_OVERFLOW));
}
glBufferSubData(Target, offset_, data_.size(), data_.data());
}
// virtual void clear() const { glClearBufferData(Target, ) }
static const GLenum Target;
protected:
using OpenGLObject::_id;
GLenum _usage = GL_STATIC_DRAW;
size_t _size;
virtual GLuint declare() override {
if (!_id) {
glGenBuffers(1, &_id);
}
return _id;
}
virtual void remove() override {
if (_id) {
glDeleteBuffers(1, &_id);
_id = 0;
}
}
};
template<typename BufferType>
const GLenum Buffer<BufferType>::Target = BufferType::Target;
template <typename BufferType>
class ImmutableBuffer : public virtual Buffer<BufferType> {
public:
using Base = Buffer<BufferType>;
using Base::Target;
using Base::bind;
ImmutableBuffer() = default;
ImmutableBuffer(size_t size_, GLenum usage_ = GL_STATIC_DRAW, GLbitfield flags_=0x0) : size(size_), Base::usage(usage_) {
glBufferStorage(Target, size, nullptr, flags_);
}
std::weak_ptr<void> map(GLbitfield access_) {
if (_map && _mapAccess != access_) { unmap(); }
_mapAccess = access_;
if (!_map) {
bind();
glMapBuffer(Target, _mapAccess);
}
return _map;
}
void unmap() {
bind();
glUnmapBuffer(Target);
_map.reset();
}
const size_t size;
protected:
using Base::_id;
void remove() { glDeleteBuffers(1, &_id); }
std::shared_ptr<void> _map = nullptr;
GLbitfield _mapAccess = 0;
};
class VertexBuffer : public virtual Buffer<VertexBuffer> {
public:
template<bool Immutable=false>
class ArrayBuffer : public Buffer<ArrayBuffer<false>> {
using Base = Buffer<ArrayBuffer<false>>;
using BufferType = Base;
static const GLenum GLTarget = GL_ARRAY_BUFFER;
protected:
public:
using Base::Base;
using Base::data;
ArrayBuffer(const ByteVector&, GLenum usage_=GL_STATIC_DRAW);
static const GLenum Target = GL_ARRAY_BUFFER;
protected:
using Base::_usage;
};
template <>
class ArrayBuffer<true>
: public virtual ArrayBuffer<false>, public virtual ImmutableBuffer<ArrayBuffer<true>> {
using Base = ArrayBuffer<false>;
using BufferType = ImmutableBuffer<ArrayBuffer<true>>;
using BufferType::BufferType;
public:
using Base::Target;
};
class ElementBuffer : public virtual Buffer<ElementBuffer> {
public:
template<bool Immutable=false>
class ElementBuffer : public virtual Buffer<ElementBuffer<false>> {
using Base = Buffer<ElementBuffer<false>>;
using BufferType = Base;
public:
using Base::Base;
static const GLenum Target = GL_ELEMENT_ARRAY_BUFFER;
static const GLenum GLTarget = GL_ELEMENT_ARRAY_BUFFER;
protected:
protected:
};
/*
class Buffer : public OpenGLObject {
public:
Buffer(
GLenum buffer_type,
GLenum usage=GL_STATIC_DRAW
) : _type(buffer_type), _usage(usage) {
glGenBuffers(1, &_id);
}
Buffer(
const RawVec& init_data,
GLenum buffer_type,
GLenum usage=GL_STATIC_DRAW
) : _type{buffer_type}, _usage{usage} { data(init_data); }
Buffer(
unsigned int size,
GLenum buffer_type,
GLenum usage=GL_STATIC_DRAW
) : Buffer(RawVec(size), buffer_type, usage) {}
template <>
class ElementBuffer<true>
: public virtual ElementBuffer<false>, public virtual ImmutableBuffer<ElementBuffer<true>> {
using Base = ElementBuffer<false>;
using BufferType = ImmutableBuffer<ElementBuffer<true>>;
Buffer(Buffer&& rhs) { *this = std::move(rhs); }
Buffer& operator=(Buffer&& rhs) {
remove();
_usage = rhs._usage;
_id = rhs._id;
rhs._id = 0;
return *this;
}
GLenum usage() const { return _usage; }
unsigned int id() const { return _id; }
GLuint size() const { return _size; }
bool isInitialized() const override { return _id && glIsBuffer(_id) && bool(_size); }
RawVec data() const {
RawVec ret(_size);
bind();
glGetBufferSubData(_type, 0, _size, ret.data());
return ret;
}
void bind() const override { glBindBuffer(_type, _id); }
void unbind() const override { glBindBuffer(_type, 0); }
GLenum usage(GLenum usage) {
_usage=usage;
data(data());
return _usage;
}
void data(const RawVec& set_data) {
if (!glfwGetCurrentContext()) {
THROW_BUFFER_ERROR("No OpenGL context.");
}
bind();
glBufferData(_type, set_data.size(), set_data.data(), _usage);
_size = set_data.size();
}
void data(const RawVec& newData, unsigned int offset) {
if (newData.size()+offset > _size) {
THROW_BUFFER_ERROR("Attempting to overflow buffer of capacity " + std::to_string(_size) + ".");
}
bind();
glBufferSubData(_type, offset, newData.size(), newData.data());
}
void data(void* src, unsigned int offset, unsigned int size) {
if (size+offset > _size) {
THROW_BUFFER_ERROR("Attempting to overflow buffer of capacity " + std::to_string(_size) + ".");
}
bind();
glBufferSubData(_type, offset, size, src);
}
protected:
virtual void remove() const override {
unbind();
if (_id) { glDeleteBuffers(1, &_id); }
}
using OpenGLObject::_id;
GLenum _type;
GLenum _usage;
unsigned int _size = 0;
};
class Texture : public OpenGLObject {
public:
Texture(GLenum tar=GL_TEXTURE_2D) : _target(tar) { glGenTextures(1, &_id); }
Texture(Texture&& rhs) { *this=std::move(rhs); }
Texture& operator=(Texture&& rhs) {
_target = rhs._target;
_id = rhs._id;
rhs._id = 0;
return *this;
}
void bind() const override { glBindTexture(_target, _id); }
void unbind() const override { glBindTexture(_target, 0); }
bool isInitialized() const override { return _id && glIsTexture(_id); }
protected:
using OpenGLObject::_id;
GLenum _target;
};
class Framebuffer : public OpenGLObject {
public:
Framebuffer(GLenum tar=GL_FRAMEBUFFER) : _target(tar) { glGenFramebuffers(1, &_id); }
Framebuffer(Framebuffer&& rhs) { *this=std::move(rhs); }
Framebuffer& operator=(Framebuffer&& rhs) {
remove();
_target = rhs._target;
_id = rhs._id;
rhs._id = 0;
return *this;
}
void bind() const override { glBindFramebuffer(_target, _id); }
void unbind() const override { glBindFramebuffer(_target, 0); }
bool isInitialized() const override { return _id && glIsFramebuffer(_id); }
GLenum status() const {
bind();
return glCheckFramebufferStatus(_target);
}
protected:
virtual void remove() const override {
unbind();
if (_id) { glDeleteFramebuffers(1, &_id); }
}
using OpenGLObject::_id;
GLenum _target;
};
class Renderbuffer : public OpenGLObject {
public:
Renderbuffer(unsigned int x, unsigned int y, GLenum format=GL_RGBA32F, unsigned int multi=0) : Renderbuffer() {
storage(x, y, format, multi);
}
Renderbuffer() { glGenRenderbuffers(1, &_id); }
Renderbuffer(Renderbuffer&& rhs) { *this = std::move(rhs); }
Renderbuffer& operator=(Renderbuffer&& rhs) {
_format = rhs._format;
_id = rhs._id;
rhs._id = 0;
return *this;
}
virtual void bind() const override { glBindRenderbuffer(GL_RENDERBUFFER, _id); }
virtual void unbind() const override { glBindRenderbuffer(GL_RENDERBUFFER, _id); }
virtual bool isInitialized() const override { return _id && glIsRenderbuffer(_id); }
void storage(unsigned int x, unsigned int y, GLenum format=GL_RGBA32F, unsigned int multi=0) {
_sizex = x;
_sizey = y;
_format = format;
_multisample = multi;
bind();
glRenderbufferStorageMultisample(GL_RENDERBUFFER, _multisample, _format, _sizex, _sizey);
}
protected:
virtual void remove() const override {
unbind();
if (_id) { glDeleteRenderbuffers(1, &_id); }
}
using OpenGLObject::_id;
GLenum _format;
unsigned int _sizex{0};
unsigned int _sizey{0};
unsigned int _multisample{0};
public:
using BufferType::BufferType;
using Base::Target;
};
*/
} // namespace NB
#endif // _NB_BUFFER