#pragma once #ifndef _NB_BUFFER #define _NB_BUFFER #include "GLLoad.hpp" #include #include #include #include #include #include #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 ByteVector vectorToRaw(const std::vector& vec) { unsigned int num_bytes = vec.size() * sizeof(T); ByteVector ret(num_bytes); memcpy(ret.data(), vec.data(), num_bytes); return ret; } template ByteVector concatVectorsToRaw(const std::vector& vec1, const std::vector& vec2) { ByteVector vec1_raw = vectorToRaw(vec1); ByteVector vec2_raw = vectorToRaw(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 std::vector 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 ret(num_elmts); memcpy(ret.data(), vec.data(), vec.size()); return ret; } class BufferError : public Error { using Base = Error; 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 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 const GLenum Buffer::Target = BufferType::Target; template class ImmutableBuffer : public virtual Buffer { public: using Base = Buffer; 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 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 _map = nullptr; GLbitfield _mapAccess = 0; }; template class ArrayBuffer : public Buffer> { using Base = Buffer>; 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 : public virtual ArrayBuffer, public virtual ImmutableBuffer> { using Base = ArrayBuffer; using BufferType = ImmutableBuffer>; using BufferType::BufferType; public: using Base::Target; }; template class ElementBuffer : public virtual Buffer> { using Base = Buffer>; using BufferType = Base; public: using Base::Base; static const GLenum Target = GL_ELEMENT_ARRAY_BUFFER; protected: }; template <> class ElementBuffer : public virtual ElementBuffer, public virtual ImmutableBuffer> { using Base = ElementBuffer; using BufferType = ImmutableBuffer>; public: using BufferType::BufferType; using Base::Target; }; } // namespace NB #endif // _NB_BUFFER