diff --git a/engine/include/engine/components/batch.h b/engine/include/engine/components/batch.h index 19af3a6..398b575 100644 --- a/engine/include/engine/components/batch.h +++ b/engine/include/engine/components/batch.h @@ -2,12 +2,15 @@ #define COMPONENT_BATCH_H_ #include +#include + +#include "engine/opengl/buffers.h" + #include "engine/export.h" namespace Core { // requires mesh component struct ENGINE_API batch { - friend class Renderer; public: // requires Transform component struct item { @@ -17,13 +20,18 @@ public: batch(); inline const unsigned int id() const { return m_id; } + inline const bool Initialized() const { return m_instanceBuffer != nullptr; } protected: static unsigned int LastID; private: unsigned int m_id; unsigned int m_instance_vbo { 0 }; unsigned int m_instance_count { 0 }; + + // TODO: use static draw when possible + std::unique_ptr m_instanceBuffer = nullptr; private: + friend class Renderer; void prepare(glm::mat4 *instances, unsigned int count); }; } diff --git a/engine/include/engine/opengl/buffers.h b/engine/include/engine/opengl/buffers.h index 1b68edc..0f62f29 100644 --- a/engine/include/engine/opengl/buffers.h +++ b/engine/include/engine/opengl/buffers.h @@ -17,15 +17,16 @@ namespace OpenGL { public: Buffer(BufferTarget target, BufferUsage usage); + inline const BufferID GetID() const { return m_buffer; } protected: void Data(void* data, size_t size); void SubData(void *data, size_t size, size_t offset); - void BindBuffer(unsigned int index); - void BindBufferRanged(unsigned int index, size_t offset, size_t size); + void BindBuffer(unsigned int index) const; + void BindBufferRanged(unsigned int index, size_t offset, size_t size) const; protected: - void Bind(); - void Unbind(); + void Bind() const; + void Unbind() const; private: BufferID m_buffer; BufferTarget m_target; @@ -36,7 +37,7 @@ namespace OpenGL { public: UniformBuffer(size_t size, unsigned int index); - void ConfigureShader(Shader& shader, const char* uniformName); + void ConfigureShader(Shader& shader, const char* uniformName) const; template void UpdateUniform(void* data, S offset) { @@ -47,6 +48,27 @@ namespace OpenGL { private: static unsigned int s_bufferNextId; }; + + class ENGINE_API ArrayBuffer : public Buffer { + public: + ArrayBuffer(BufferUsage usage); + }; + + class ENGINE_API InstanceBuffer : public ArrayBuffer { + public: + InstanceBuffer(BufferUsage usage); + + void Data(void *data, size_t size) { + Buffer::Data(data, size); + } + + void SubData(void *data, size_t size, size_t offset) { + Buffer::SubData(data, size, offset); + } + + inline void StartConfigure() const { Bind(); } + inline void EndConfigure() const { Unbind(); } + }; } // namespace OpenGL } // namespace Core diff --git a/engine/include/engine/renderer/wavefront.h b/engine/include/engine/renderer/wavefront.h index b763996..0d3da1f 100644 --- a/engine/include/engine/renderer/wavefront.h +++ b/engine/include/engine/renderer/wavefront.h @@ -11,6 +11,7 @@ #include "engine/renderer/renderer.h" #include "engine/renderer/material.h" #include "engine/renderer/mesh.h" +#include "engine/opengl/buffers.h" #include "engine/export.h" @@ -43,7 +44,7 @@ public: void Render(Shader& shader, unsigned int count); [[nodiscard]] inline const std::string Name() const { return m_name; } protected: - void EnableBatch(unsigned int instanceVBO); + void EnableBatch(const OpenGL::InstanceBuffer* instanceBuffer); private: std::string m_name; std::vector m_vertices; diff --git a/engine/src/components/batch.cpp b/engine/src/components/batch.cpp index d83d0fe..c22ea90 100644 --- a/engine/src/components/batch.cpp +++ b/engine/src/components/batch.cpp @@ -11,24 +11,19 @@ batch::batch() { } void batch::prepare(glm::mat4 *instances, unsigned int count) { - if (m_instance_vbo == 0) { - glGenBuffers(1, &m_instance_vbo); - glBindBuffer(GL_ARRAY_BUFFER, m_instance_vbo); - // Allocate *once*, no data yet — just reserve space - glBufferData(GL_ARRAY_BUFFER, sizeof(glm::mat4) * count, nullptr, GL_DYNAMIC_DRAW); + if (!m_instanceBuffer) { + m_instanceBuffer = std::make_unique(GL_DYNAMIC_DRAW); + m_instanceBuffer->Data(nullptr, sizeof(glm::mat4) * count); m_instance_count = count; } else if (count > m_instance_count) { // Optional: reallocate only if you *really* have more instances than before glBindBuffer(GL_ARRAY_BUFFER, m_instance_vbo); - glBufferData(GL_ARRAY_BUFFER, sizeof(glm::mat4) * count, nullptr, GL_DYNAMIC_DRAW); + m_instanceBuffer->Data(nullptr, sizeof(glm::mat4) * count); m_instance_count = count; - } else { - glBindBuffer(GL_ARRAY_BUFFER, m_instance_vbo); } // Just update the data region — much cheaper - glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(glm::mat4) * count, instances); - glBindBuffer(GL_ARRAY_BUFFER, 0); + m_instanceBuffer->SubData(instances, sizeof(glm::mat4) * count, 0); } } diff --git a/engine/src/opengl/buffers.cpp b/engine/src/opengl/buffers.cpp index 7b32e8b..6123d6c 100644 --- a/engine/src/opengl/buffers.cpp +++ b/engine/src/opengl/buffers.cpp @@ -13,11 +13,11 @@ namespace OpenGL { Unbind(); } - void Buffer::Bind() { + void Buffer::Bind() const { glBindBuffer(m_target, m_buffer); } - void Buffer::Unbind() { + void Buffer::Unbind() const { glBindBuffer(m_target, 0); } @@ -33,13 +33,13 @@ namespace OpenGL { Unbind(); } - void Buffer::BindBuffer(unsigned int index) { + void Buffer::BindBuffer(unsigned int index) const { Bind(); glBindBufferBase(m_target, index, m_buffer); Unbind(); } - void Buffer::BindBufferRanged(unsigned int index, size_t offset, size_t size) { + void Buffer::BindBufferRanged(unsigned int index, size_t offset, size_t size) const { Bind(); glBindBufferRange(m_target, index, m_buffer, offset, size); Unbind(); @@ -55,11 +55,17 @@ namespace OpenGL { BindBuffer(m_uniformBinding); } - void UniformBuffer::ConfigureShader(Shader& shader, const char* uniformName) { + void UniformBuffer::ConfigureShader(Shader& shader, const char* uniformName) const { auto uniformIndex = glGetUniformBlockIndex(shader.GetID(), uniformName); glUniformBlockBinding(shader.GetID(), uniformIndex, m_uniformBinding); } + ArrayBuffer::ArrayBuffer(BufferUsage usage) + : Buffer(GL_ARRAY_BUFFER, usage) {} + + InstanceBuffer::InstanceBuffer(BufferUsage usage) + : ArrayBuffer(usage) {} + } // namespace OpenGL } // namespace Core \ No newline at end of file diff --git a/engine/src/renderer/renderer.cpp b/engine/src/renderer/renderer.cpp index a8ff285..b7544d4 100644 --- a/engine/src/renderer/renderer.cpp +++ b/engine/src/renderer/renderer.cpp @@ -166,11 +166,11 @@ void Renderer::RenderScene(Shader &shader) { models.push_back(itemModel); } - auto prevInstanceVBO = b.m_instance_vbo; + auto prevState = b.Initialized(); b.prepare(models.data(), models.size()); - if (prevInstanceVBO <= 0) { + if (!prevState) { std::cout << "[DEBUG] enabling batch" << std::endl; - m.object->EnableBatch(b.m_instance_vbo); + m.object->EnableBatch(b.m_instanceBuffer.get()); } m.object->Render(shader, batchItems.size()); } diff --git a/engine/src/renderer/wavefront.cpp b/engine/src/renderer/wavefront.cpp index d36d875..248c682 100644 --- a/engine/src/renderer/wavefront.cpp +++ b/engine/src/renderer/wavefront.cpp @@ -397,19 +397,19 @@ Object* Object::LoadFile(const std::string& filename) { return obj; } -void Object::EnableBatch(unsigned int instanceVBO) { +void Object::EnableBatch(const OpenGL::InstanceBuffer* instanceBuffer) { for (auto &mesh : m_meshes) { mesh.Bind(); - glBindBuffer(GL_ARRAY_BUFFER, instanceVBO); - std::size_t vec4Size = sizeof(glm::vec4); - for (int i = 0; i < 4; ++i) { - glEnableVertexAttribArray(3 + i); // use locations 3,4,5,6 for instance matrix - glVertexAttribPointer(3 + i, 4, GL_FLOAT, GL_FALSE, - sizeof(glm::mat4), (void*)(i * vec4Size)); - glVertexAttribDivisor(3 + i, 1); // IMPORTANT: one per instance, not per vertex - } - glBindBuffer(GL_ARRAY_BUFFER, 0); + instanceBuffer->StartConfigure(); + std::size_t vec4Size = sizeof(glm::vec4); + for (int i = 0; i < 4; ++i) { + glEnableVertexAttribArray(3 + i); // use locations 3,4,5,6 for instance matrix + glVertexAttribPointer(3 + i, 4, GL_FLOAT, GL_FALSE, + sizeof(glm::mat4), (void*)(i * vec4Size)); + glVertexAttribDivisor(3 + i, 1); // IMPORTANT: one per instance, not per vertex + } + instanceBuffer->EndConfigure(); mesh.Unbind(); }