#pragma once /* * Copyright (C) 2024 Brett Terpstra * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef BLT_GFX_VBO_H #define BLT_GFX_VBO_H #include #include namespace blt::gfx { class unique_vbo_t; namespace detail { /** * So long as this class is called from vbo.bind(), it is always valid to chain any internal functions. * This system is designed to be foolproof, so don't get too clever */ class vbo_context_t { friend unique_vbo_t; public: vbo_context_t(const vbo_context_t& copy) = delete; vbo_context_t(vbo_context_t&& move) = delete; vbo_context_t& operator=(const vbo_context_t& copy) = delete; vbo_context_t& operator=(vbo_context_t&& move) = delete; /** * By default, the VBO is bound when this class is constructed (this class should only be constructed through the VBO bind() method) * * It is very unlikely that you need this method! */ vbo_context_t& bind(); vbo_context_t& unbind(); /** * Reserves a chunk of GPU memory for future use */ vbo_context_t& resize(GLsizeiptr size, GLint mem_type); /** * Uploads a chunk of memory to the GPU. If the existing VBO has enough space, the memory will be reused. */ vbo_context_t& upload(size_t size, const void* ptr, GLint mem_type); /** * Uploads a chunk of memory to the GPU. If the existing VBO has enough space, the memory will be reused. */ template , bool> = true> vbo_context_t& upload(const size_t size, const T* ptr, const GLint mem_type) { return upload(size, static_cast(ptr), mem_type); } /** * Updates an internal segment of the VBO. This function will never reallocate. */ vbo_context_t& update(size_t offset, size_t size, const void* ptr); /** * Updates an internal segment of the VBO. This function will never reallocate. */ template , bool> = true> vbo_context_t& update(const size_t offset, const size_t size, T* ptr) { return update(offset, size, static_cast(ptr)); } private: [[nodiscard]] bool is_bound() const; explicit vbo_context_t(unique_vbo_t& vbo): vbo(vbo) { bind(); } unique_vbo_t& vbo; }; } class unique_vbo_t { friend class detail::vbo_context_t; public: explicit unique_vbo_t(const GLuint type): vboID(0), buffer_type(type) { glGenBuffers(1, &*vboID); } unique_vbo_t(const unique_vbo_t&) = delete; unique_vbo_t& operator=(const unique_vbo_t&) = delete; unique_vbo_t(unique_vbo_t&& other) noexcept: vboID(std::exchange(other.vboID, std::nullopt)), buffer_type(other.buffer_type), size(other.size), memory_type(other.memory_type) {} unique_vbo_t& operator=(unique_vbo_t&& other) noexcept { vboID = std::exchange(other.vboID, vboID); buffer_type = std::exchange(other.buffer_type, buffer_type); size = std::exchange(other.size, size); memory_type = std::exchange(other.memory_type, memory_type); return *this; } /** * Changes the internal buffer type of this VBO */ GLuint change_type(const GLuint type) { return std::exchange(this->buffer_type, type); } /** * This function binds the VBO to the current buffer_type slot and returns an object which allows you to modify or use this VBO. * This allows you to use the VBO without worrying about whether an operation is valid in this context. * As so long as you use this object in line, or without binding other VBOs to the same buffer_type * (violating the contracts this function attempts to create) then all functions on the associated object are valid to call. * * You can enable the flag BLT_DEBUG_CONTRACTS which will validate VBO bind state making most of ^ irrelevant */ detail::vbo_context_t bind(); [[nodiscard]] auto native_handle() const { return vboID; } [[nodiscard]] GLsizeiptr get_size() const { return size; } [[nodiscard]] GLint get_memory_type() const { return memory_type; } [[nodiscard]] GLuint get_buffer_type() const { return buffer_type; } ~unique_vbo_t() { if (vboID) glDeleteBuffers(1, &*vboID); } private: std::optional vboID; GLuint buffer_type; GLsizeiptr size = 0; GLint memory_type = 0; }; class unique_ssbo_t : public unique_vbo_t { public: unique_ssbo_t(): unique_vbo_t{GL_SHADER_STORAGE_BUFFER} {} }; class unique_ebo_t : public unique_vbo_t { public: unique_ebo_t(): unique_vbo_t{GL_ELEMENT_ARRAY_BUFFER} {} }; class unique_ubo_t : public unique_vbo_t { public: explicit unique_ubo_t(const i32 location): unique_vbo_t{GL_UNIFORM_BUFFER} {set_location(location);} void set_location(i32 new_location); [[nodiscard]] i32 get_location() const { return location; } private: i32 location = 0; }; } #endif //BLT_GFX_VBO_H