#pragma once #include "common.h" namespace gl { class buffer { public: enum class target { pixel_pack = GL_PIXEL_PACK_BUFFER, pixel_unpack = GL_PIXEL_UNPACK_BUFFER, array = GL_ARRAY_BUFFER, element_array = GL_ELEMENT_ARRAY_BUFFER, uniform = GL_UNIFORM_BUFFER, texture = GL_TEXTURE_BUFFER, ssbo = GL_SHADER_STORAGE_BUFFER }; enum class access { read = GL_MAP_READ_BIT, write = GL_MAP_WRITE_BIT, read_write = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT }; enum class memory_type { undefined = 0, local = 1, host_visible = 2 }; class save_binding_state { GLint m_last_binding; GLenum m_target; public: save_binding_state(target target_, const buffer& new_state) : save_binding_state(target_) { new_state.bind(target_); } save_binding_state(target target_) { GLenum pname{}; switch (target_) { case target::pixel_pack: pname = GL_PIXEL_PACK_BUFFER_BINDING; break; case target::pixel_unpack: pname = GL_PIXEL_UNPACK_BUFFER_BINDING; break; case target::array: pname = GL_ARRAY_BUFFER_BINDING; break; case target::element_array: pname = GL_ELEMENT_ARRAY_BUFFER_BINDING; break; case target::uniform: pname = GL_UNIFORM_BUFFER_BINDING; break; case target::texture: pname = GL_TEXTURE_BUFFER_BINDING; break; case target::ssbo: pname = GL_SHADER_STORAGE_BUFFER_BINDING; break; default: fmt::throw_exception("Invalid binding state target (0x%x)", static_cast(target_)); } glGetIntegerv(pname, &m_last_binding); m_target = static_cast(target_); } ~save_binding_state() { glBindBuffer(m_target, m_last_binding); } }; protected: GLuint m_id = GL_NONE; GLsizeiptr m_size = 0; target m_target = target::array; memory_type m_memory_type = memory_type::undefined; void allocate(GLsizeiptr size, const void* data_, memory_type type, GLenum usage); public: buffer() = default; buffer(const buffer&) = delete; ~buffer(); void recreate(); void recreate(GLsizeiptr size, const void* data = nullptr); void create(); void create(GLsizeiptr size, const void* data_ = nullptr, memory_type type = memory_type::local, GLenum usage = GL_STREAM_DRAW); void create(target target_, GLsizeiptr size, const void* data_ = nullptr, memory_type type = memory_type::local, GLenum usage = GL_STREAM_DRAW); void remove(); void bind(target target_) const { glBindBuffer(static_cast(target_), m_id); } void bind() const { bind(current_target()); } void data(GLsizeiptr size, const void* data_ = nullptr, GLenum usage = GL_STREAM_DRAW); void sub_data(GLsizeiptr offset, GLsizeiptr length, GLvoid* data); GLubyte* map(GLsizeiptr offset, GLsizeiptr length, access access_); void unmap(); void bind_range(u32 index, u32 offset, u32 size) const; void bind_range(target target_, u32 index, u32 offset, u32 size) const; void copy_to(buffer* other, u64 src_offset, u64 dst_offset, u64 size); target current_target() const { return m_target; } GLsizeiptr size() const { return m_size; } uint id() const { return m_id; } void set_id(uint id) { m_id = id; } bool created() const { return m_id != GL_NONE; } explicit operator bool() const { return created(); } }; class buffer_view { buffer* m_buffer = nullptr; u32 m_offset = 0; u32 m_range = 0; GLenum m_format = GL_R8UI; public: buffer_view(buffer* _buffer, u32 offset, u32 range, GLenum format = GL_R8UI) : m_buffer(_buffer), m_offset(offset), m_range(range), m_format(format) {} buffer_view() = default; void update(buffer* _buffer, u32 offset, u32 range, GLenum format = GL_R8UI); u32 offset() const { return m_offset; } u32 range() const { return m_range; } u32 format() const { return m_format; } buffer* value() const { return m_buffer; } bool in_range(u32 address, u32 size, u32& new_offset) const; }; }