diff --git a/.idea/editor.xml b/.idea/editor.xml
index 5bf27ce..907ea1b 100644
--- a/.idea/editor.xml
+++ b/.idea/editor.xml
@@ -283,8 +283,11 @@
+
+
+
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 76081bf..39685cc 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.25)
-project(gpu-particles VERSION 0.0.11)
+project(gpu-particles VERSION 0.0.12)
option(ENABLE_ADDRSAN "Enable the address sanitizer" OFF)
option(ENABLE_UBSAN "Enable the ub sanitizer" OFF)
diff --git a/include/vao.h b/include/vao.h
index 3f249aa..0023744 100644
--- a/include/vao.h
+++ b/include/vao.h
@@ -19,13 +19,72 @@
#ifndef BLT_GFX_VAO_H
#define BLT_GFX_VAO_H
+#include
#include
+#include
namespace blt::gfx
{
+ class unique_vao_t;
+
+ namespace detail
+ {
+ struct vao_vbo_storage_t
+ {
+ std::unique_ptr vbo;
+ u32 attribute_number = 0;
+
+ [[nodiscard]] bool is_element() const
+ {
+ return vbo->get_buffer_type() == GL_ELEMENT_ARRAY_BUFFER;
+ }
+
+ explicit vao_vbo_storage_t(unique_vbo_t&& vbo): vbo(std::make_unique(std::move(vbo)))
+ {}
+ };
+
+ class vao_vbo_context_t
+ {
+ friend class vao_context_t;
+ public:
+
+ private:
+ vao_vbo_context_t(unique_vao_t& vao, vao_vbo_storage_t& vbo): vbo(vbo), vao(vao)
+ {}
+
+ vao_vbo_storage_t& vbo;
+ unique_vao_t& vao;
+ };
+
+ class vao_context_t
+ {
+ friend vao_vbo_context_t;
+ friend class unique_vao_t;
+
+ public:
+ vao_context_t& bind();
+
+ vao_context_t& unbind();
+
+ vao_vbo_context_t attach_vbo(unique_vbo_t&& vbo) const;
+
+ private:
+ [[nodiscard]] bool is_bound() const;
+
+ explicit vao_context_t(unique_vao_t& vao): vao(vao)
+ {
+ bind();
+ }
+
+ unique_vao_t& vao;
+ };
+ }
class unique_vao_t
{
+ friend detail::vao_vbo_context_t;
+ friend detail::vao_context_t;
+
public:
unique_vao_t(): vaoID(0)
{
@@ -45,6 +104,8 @@ namespace blt::gfx
return *this;
}
+ detail::vao_context_t bind();
+
~unique_vao_t()
{
if (vaoID)
@@ -53,8 +114,8 @@ namespace blt::gfx
private:
std::optional vaoID;
+ std::vector vbo_list;
};
-
}
#endif //BLT_GFX_VAO_H
diff --git a/include/vbo.h b/include/vbo.h
index bb35434..91cd0c2 100644
--- a/include/vbo.h
+++ b/include/vbo.h
@@ -36,12 +36,8 @@ namespace blt::gfx
*/
class vbo_context_t
{
+ friend unique_vbo_t;
public:
- explicit vbo_context_t(unique_vbo_t& vbo): vbo(vbo)
- {
- bind();
- }
-
/**
* By default, the VBO is bound when this class is constructed (this class should only be constructed through the VBO bind() method)
*
@@ -93,6 +89,13 @@ namespace blt::gfx
}
private:
+ [[nodiscard]] bool is_bound() const;
+
+ explicit vbo_context_t(unique_vbo_t& vbo): vbo(vbo)
+ {
+ bind();
+ }
+
unique_vbo_t& vbo;
};
}
@@ -137,14 +140,31 @@ namespace blt::gfx
* 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
*/
- [[nodiscard]] detail::vbo_context_t bind();
+ 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)
diff --git a/src/vao.cpp b/src/vao.cpp
index 931cdeb..352352a 100644
--- a/src/vao.cpp
+++ b/src/vao.cpp
@@ -16,8 +16,51 @@
* along with this program. If not, see .
*/
#include
+#include
+#include
+#include
namespace blt::gfx
{
+ #define BLT_DEBUG_LEVEL BLT_DEBUG_CONTRACTS
-}
\ No newline at end of file
+ #if blt_debug_has_flag(BLT_DEBUG_CONTRACTS)
+ GLuint bound_vao_id;
+ #endif
+
+ detail::vao_context_t& detail::vao_context_t::bind()
+ {
+ BLT_CONTRACT(vao.vaoID, "Expected VAO to have an associated VAO ID!");
+ glBindVertexArray(*vao.vaoID);
+ bound_vao_id = *vao.vaoID;
+ return *this;
+ }
+
+ detail::vao_context_t& detail::vao_context_t::unbind() // NOLINT
+ {
+ glBindVertexArray(0);
+ bound_vao_id = 0;
+ return *this;
+ }
+
+ detail::vao_vbo_context_t detail::vao_context_t::attach_vbo(unique_vbo_t&& vbo) const
+ {
+ BLT_CONTRACT(vao.vaoID, "Expected VAO to have an associated VAO ID!");
+ BLT_CONTRACT(is_bound(), "Expected VAO to be bound before attaching VBO! (If you are using this API correctly, this has been done for you!)");
+
+ auto& vbo_storage = vao.vbo_list.emplace_back(std::move(vbo));
+ vbo_storage.vbo->bind();
+ return vao_vbo_context_t{vao, vbo_storage};
+ }
+
+ bool detail::vao_context_t::is_bound() const
+ {
+ return *vao.vaoID == bound_vao_id;
+ }
+
+ detail::vao_context_t unique_vao_t::bind()
+ {
+ BLT_CONTRACT(glfwGetCurrentContext() != nullptr, "Expected active OpenGL context!");
+ return detail::vao_context_t{*this};
+ }
+}
diff --git a/src/vbo.cpp b/src/vbo.cpp
index 64f2da2..424b0b0 100644
--- a/src/vbo.cpp
+++ b/src/vbo.cpp
@@ -18,26 +18,38 @@
#include
#include
#include
+#include
namespace blt::gfx
{
+ #if blt_debug_has_flag(BLT_DEBUG_CONTRACTS)
+ static hashmap_t bound_vbo_ids;
+ #endif
+
namespace detail
{
vbo_context_t& vbo_context_t::bind()
{
- BLT_CONTRACT(vbo.vboID, "Expected VBO to have an assoicated VBO ID!");
+ BLT_CONTRACT(vbo.vboID, "Expected VBO to have an associated VBO ID!");
glBindBuffer(vbo.buffer_type, *vbo.vboID);
+ #if blt_debug_has_flag(BLT_DEBUG_CONTRACTS)
+ bound_vbo_ids[vbo.buffer_type] = *vbo.vboID;
+ #endif
return *this;
}
vbo_context_t& vbo_context_t::unbind()
{
glBindBuffer(vbo.buffer_type, 0);
+ #if blt_debug_has_flag(BLT_DEBUG_CONTRACTS)
+ bound_vbo_ids[vbo.buffer_type] = 0;
+ #endif
return *this;
}
vbo_context_t& vbo_context_t::resize(const GLsizeiptr size, const GLint mem_type)
{
+ BLT_CONTRACT(is_bound(), "Expected VBO to be bound before resizing!");
vbo.size = size;
vbo.memory_type = mem_type;
glBufferData(vbo.buffer_type, size, nullptr, mem_type);
@@ -46,6 +58,7 @@ namespace blt::gfx
vbo_context_t& vbo_context_t::upload(const size_t size, const void* ptr, const GLint mem_type)
{
+ BLT_CONTRACT(is_bound(), "Expected VBO to be bound before uploading!");
if (mem_type != vbo.memory_type || static_cast(vbo.size) < size)
{
vbo.size = static_cast(size);
@@ -60,9 +73,19 @@ namespace blt::gfx
vbo_context_t& vbo_context_t::update(const size_t offset, const size_t size, const void* ptr)
{
+ BLT_CONTRACT(is_bound(), "Expected VBO to be bound before updating!");
glBufferSubData(vbo.buffer_type, static_cast(offset), static_cast(size), ptr);
return *this;
}
+
+ bool vbo_context_t::is_bound() const
+ {
+ #if blt_debug_has_flag(BLT_DEBUG_CONTRACTS)
+ return bound_vbo_ids[vbo.buffer_type] == *vbo.vboID;
+ #else
+ return true;
+ #endif
+ }
}
detail::vbo_context_t unique_vbo_t::bind()