NBEngine/engine/NBGraphics/Buffers.hpp

242 lines
6.0 KiB
C++

#pragma once
#ifndef _NB_BUFFER
#define _NB_BUFFER
#include "GLLoad.hpp"
#include <memory>
#include <string>
#include <NBCore/Errors.hpp>
#include <NBCore/Logger.hpp>
#include <NBCore/Utils.hpp>
#include "OGLObjects.hpp"
namespace nb {
extern const std::unordered_map<GLenum, std::string> BufferTypes;
class BufferError : public Error<BufferError> {
protected:
using Base = Error<BufferError>;
using Base::Base;
public:
enum Codes : unsigned int {
UNDEFINED,
DATA_OVERFLOW,
INVALID_BUFFER,
HANDLE_OVERWRITE,
};
static const std::string type;
static const ErrorCodeMap ErrorMessages;
};
template <typename BufferType>
class Buffer : public OpenGLObject {
using Codes = BufferError::Codes;
public:
using OpenGLObject::OpenGLObject;
Buffer() = default;
Buffer(Buffer&& other) {
*this = std::move(other);
}
Buffer& operator=(Buffer&& rhs) {
auto targ_name = BufferTypes.at(Target);
if (Target != rhs.Target) {
THROW(BufferError(
Codes::INVALID_BUFFER,
"w/ ("+targ_name+") := ("+BufferTypes.at(rhs.Target)+")"
));
}
//nb::logger.log(std::to_string(_id) + ":=" + std::to_string(rhs._id));
if (_id) {
THROW(BufferError(
Codes::HANDLE_OVERWRITE,
"w/ BufferType = " + targ_name
));
}
_id = rhs._id;
_usage = rhs._usage;
_size = rhs._size;
rhs._id = 0;
rhs._usage = GL_STATIC_DRAW;
rhs._size = 0;
return *this;
}
virtual void bind() const override {
if (_id) {
glBindBuffer(Target, _id);
} else {
WARN(BufferError(
Codes::INVALID_BUFFER,
"w/ BufferType " + BufferTypes.at(Target)
), 0xFE);
}
}
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(data_.data(), data_.size(), 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;
std::shared_ptr<void> _map = nullptr;
GLbitfield _mapAccess = 0;
};
template<bool Immutable=false>
class ArrayBuffer : public Buffer<ArrayBuffer<false>> {
using Base = Buffer<ArrayBuffer<false>>;
using BufferType = Base;
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;
};
template<bool Immutable=false>
class ElementBuffer : public Buffer<ElementBuffer<false>> {
using Base = Buffer<ElementBuffer<false>>;
using BufferType = Base;
public:
using Base::Base;
static const GLenum Target = GL_ELEMENT_ARRAY_BUFFER;
protected:
};
template <>
class ElementBuffer<true>
: public virtual ElementBuffer<false>, public virtual ImmutableBuffer<ElementBuffer<true>> {
using Base = ElementBuffer<false>;
using BufferType = ImmutableBuffer<ElementBuffer<true>>;
public:
using BufferType::BufferType;
using Base::Target;
};
} // namespace NB
#endif // _NB_BUFFER