275 lines
6.9 KiB
C++
275 lines
6.9 KiB
C++
#pragma once
|
|
#ifndef _NB_BUFFER
|
|
#define _NB_BUFFER
|
|
|
|
#include "GLLoad.hpp"
|
|
|
|
#include <memory>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <NBCore/Errors.hpp>
|
|
#include <NBCore/Logger.hpp>
|
|
#include <NBCore/Utils.hpp>
|
|
|
|
#include "OGLObjects.hpp"
|
|
|
|
namespace nb {
|
|
|
|
static uint8_t GLSLTypeSize(GLenum type) {
|
|
switch(type) {
|
|
case GL_SHORT:
|
|
case GL_UNSIGNED_SHORT:
|
|
case GL_HALF_FLOAT:
|
|
return 2;
|
|
break;
|
|
case GL_INT:
|
|
case GL_UNSIGNED_INT:
|
|
case GL_FLOAT:
|
|
return 4;
|
|
break;
|
|
case GL_DOUBLE:
|
|
return 8;
|
|
break;
|
|
default:
|
|
return 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
ByteVector vectorToRaw(const std::vector<T>& vec) {
|
|
unsigned int num_bytes = vec.size() * sizeof(T);
|
|
ByteVector ret(num_bytes);
|
|
memcpy(ret.data(), vec.data(), num_bytes);
|
|
return ret;
|
|
}
|
|
|
|
template<typename T, typename S>
|
|
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();
|
|
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 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()) + ">."));
|
|
}
|
|
unsigned int num_elmts = vec.size() / sizeof(T);
|
|
std::vector<T> ret(num_elmts);
|
|
memcpy(ret.data(), vec.data(), vec.size());
|
|
|
|
return ret;
|
|
}
|
|
|
|
class BufferError : public Error<BufferError> {
|
|
using Base = Error<BufferError>;
|
|
|
|
public:
|
|
using Base::Base;
|
|
|
|
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) {
|
|
if (_id) { THROW(BufferError(Codes::HANDLE_OVERWRITE)); }
|
|
_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);
|
|
}
|
|
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(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 virtual 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
|