Compare commits

...

2 Commits

Author SHA1 Message Date
c6d83c1b51 feat: destructor 2025-10-24 13:17:15 +02:00
e459fea503 feat: instance + array buffer 2025-10-24 13:06:40 +02:00
7 changed files with 76 additions and 35 deletions

View File

@ -2,12 +2,15 @@
#define COMPONENT_BATCH_H_ #define COMPONENT_BATCH_H_
#include <glm/mat4x4.hpp> #include <glm/mat4x4.hpp>
#include <memory>
#include "engine/opengl/buffers.h"
#include "engine/export.h" #include "engine/export.h"
namespace Core { namespace Core {
// requires mesh component // requires mesh component
struct ENGINE_API batch { struct ENGINE_API batch {
friend class Renderer;
public: public:
// requires Transform component // requires Transform component
struct item { struct item {
@ -17,13 +20,18 @@ public:
batch(); batch();
inline const unsigned int id() const { return m_id; } inline const unsigned int id() const { return m_id; }
inline const bool Initialized() const { return m_instanceBuffer != nullptr; }
protected: protected:
static unsigned int LastID; static unsigned int LastID;
private: private:
unsigned int m_id; unsigned int m_id;
unsigned int m_instance_vbo { 0 }; unsigned int m_instance_vbo { 0 };
unsigned int m_instance_count { 0 }; unsigned int m_instance_count { 0 };
// TODO: use static draw when possible
std::unique_ptr<OpenGL::InstanceBuffer> m_instanceBuffer = nullptr;
private: private:
friend class Renderer;
void prepare(glm::mat4 *instances, unsigned int count); void prepare(glm::mat4 *instances, unsigned int count);
}; };
} }

View File

@ -16,16 +16,19 @@ namespace OpenGL {
class ENGINE_API Buffer { class ENGINE_API Buffer {
public: public:
Buffer(BufferTarget target, BufferUsage usage); Buffer(BufferTarget target, BufferUsage usage);
Buffer(BufferTarget target);
~Buffer();
inline const BufferID GetID() const { return m_buffer; }
protected: protected:
void Data(void* data, size_t size); void Data(void* data, size_t size);
void SubData(void *data, size_t size, size_t offset); void SubData(void *data, size_t size, size_t offset);
void BindBuffer(unsigned int index); void BindBuffer(unsigned int index) const;
void BindBufferRanged(unsigned int index, size_t offset, size_t size); void BindBufferRanged(unsigned int index, size_t offset, size_t size) const;
protected: protected:
void Bind(); void Bind() const;
void Unbind(); void Unbind() const;
private: private:
BufferID m_buffer; BufferID m_buffer;
BufferTarget m_target; BufferTarget m_target;
@ -36,7 +39,7 @@ namespace OpenGL {
public: public:
UniformBuffer(size_t size, unsigned int index); UniformBuffer(size_t size, unsigned int index);
void ConfigureShader(Shader& shader, const char* uniformName); void ConfigureShader(Shader& shader, const char* uniformName) const;
template<typename T, typename S = size_t> template<typename T, typename S = size_t>
void UpdateUniform(void* data, S offset) { void UpdateUniform(void* data, S offset) {
@ -47,6 +50,27 @@ namespace OpenGL {
private: private:
static unsigned int s_bufferNextId; 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 OpenGL
} // namespace Core } // namespace Core

View File

@ -11,6 +11,7 @@
#include "engine/renderer/renderer.h" #include "engine/renderer/renderer.h"
#include "engine/renderer/material.h" #include "engine/renderer/material.h"
#include "engine/renderer/mesh.h" #include "engine/renderer/mesh.h"
#include "engine/opengl/buffers.h"
#include "engine/export.h" #include "engine/export.h"
@ -43,7 +44,7 @@ public:
void Render(Shader& shader, unsigned int count); void Render(Shader& shader, unsigned int count);
[[nodiscard]] inline const std::string Name() const { return m_name; } [[nodiscard]] inline const std::string Name() const { return m_name; }
protected: protected:
void EnableBatch(unsigned int instanceVBO); void EnableBatch(const OpenGL::InstanceBuffer* instanceBuffer);
private: private:
std::string m_name; std::string m_name;
std::vector<glm::vec3> m_vertices; std::vector<glm::vec3> m_vertices;

View File

@ -11,24 +11,19 @@ batch::batch() {
} }
void batch::prepare(glm::mat4 *instances, unsigned int count) { void batch::prepare(glm::mat4 *instances, unsigned int count) {
if (m_instance_vbo == 0) { if (!m_instanceBuffer) {
glGenBuffers(1, &m_instance_vbo); m_instanceBuffer = std::make_unique<OpenGL::InstanceBuffer>(GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, m_instance_vbo); m_instanceBuffer->Data(nullptr, sizeof(glm::mat4) * count);
// Allocate *once*, no data yet — just reserve space
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::mat4) * count, nullptr, GL_DYNAMIC_DRAW);
m_instance_count = count; m_instance_count = count;
} else if (count > m_instance_count) { } else if (count > m_instance_count) {
// Optional: reallocate only if you *really* have more instances than before // Optional: reallocate only if you *really* have more instances than before
glBindBuffer(GL_ARRAY_BUFFER, m_instance_vbo); 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; m_instance_count = count;
} else {
glBindBuffer(GL_ARRAY_BUFFER, m_instance_vbo);
} }
// Just update the data region — much cheaper // Just update the data region — much cheaper
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(glm::mat4) * count, instances); m_instanceBuffer->SubData(instances, sizeof(glm::mat4) * count, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
} }
} }

View File

@ -13,11 +13,18 @@ namespace OpenGL {
Unbind(); Unbind();
} }
void Buffer::Bind() { Buffer::Buffer(BufferTarget target)
: Buffer(target, GL_STATIC_DRAW) {}
Buffer::~Buffer() {
glDeleteBuffers(1, &m_buffer);
}
void Buffer::Bind() const {
glBindBuffer(m_target, m_buffer); glBindBuffer(m_target, m_buffer);
} }
void Buffer::Unbind() { void Buffer::Unbind() const {
glBindBuffer(m_target, 0); glBindBuffer(m_target, 0);
} }
@ -33,13 +40,13 @@ namespace OpenGL {
Unbind(); Unbind();
} }
void Buffer::BindBuffer(unsigned int index) { void Buffer::BindBuffer(unsigned int index) const {
Bind(); Bind();
glBindBufferBase(m_target, index, m_buffer); glBindBufferBase(m_target, index, m_buffer);
Unbind(); 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(); Bind();
glBindBufferRange(m_target, index, m_buffer, offset, size); glBindBufferRange(m_target, index, m_buffer, offset, size);
Unbind(); Unbind();
@ -55,11 +62,17 @@ namespace OpenGL {
BindBuffer(m_uniformBinding); 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); auto uniformIndex = glGetUniformBlockIndex(shader.GetID(), uniformName);
glUniformBlockBinding(shader.GetID(), uniformIndex, m_uniformBinding); glUniformBlockBinding(shader.GetID(), uniformIndex, m_uniformBinding);
} }
ArrayBuffer::ArrayBuffer(BufferUsage usage)
: Buffer(GL_ARRAY_BUFFER, usage) {}
InstanceBuffer::InstanceBuffer(BufferUsage usage)
: ArrayBuffer(usage) {}
} // namespace OpenGL } // namespace OpenGL
} // namespace Core } // namespace Core

View File

@ -166,11 +166,11 @@ void Renderer::RenderScene(Shader &shader) {
models.push_back(itemModel); models.push_back(itemModel);
} }
auto prevInstanceVBO = b.m_instance_vbo; auto prevState = b.Initialized();
b.prepare(models.data(), models.size()); b.prepare(models.data(), models.size());
if (prevInstanceVBO <= 0) { if (!prevState) {
std::cout << "[DEBUG] enabling batch" << std::endl; 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()); m.object->Render(shader, batchItems.size());
} }

View File

@ -397,19 +397,19 @@ Object* Object::LoadFile(const std::string& filename) {
return obj; return obj;
} }
void Object::EnableBatch(unsigned int instanceVBO) { void Object::EnableBatch(const OpenGL::InstanceBuffer* instanceBuffer) {
for (auto &mesh : m_meshes) { for (auto &mesh : m_meshes) {
mesh.Bind(); mesh.Bind();
glBindBuffer(GL_ARRAY_BUFFER, instanceVBO); instanceBuffer->StartConfigure();
std::size_t vec4Size = sizeof(glm::vec4); std::size_t vec4Size = sizeof(glm::vec4);
for (int i = 0; i < 4; ++i) { for (int i = 0; i < 4; ++i) {
glEnableVertexAttribArray(3 + i); // use locations 3,4,5,6 for instance matrix glEnableVertexAttribArray(3 + i); // use locations 3,4,5,6 for instance matrix
glVertexAttribPointer(3 + i, 4, GL_FLOAT, GL_FALSE, glVertexAttribPointer(3 + i, 4, GL_FLOAT, GL_FALSE,
sizeof(glm::mat4), (void*)(i * vec4Size)); sizeof(glm::mat4), (void*)(i * vec4Size));
glVertexAttribDivisor(3 + i, 1); // IMPORTANT: one per instance, not per vertex glVertexAttribDivisor(3 + i, 1); // IMPORTANT: one per instance, not per vertex
} }
glBindBuffer(GL_ARRAY_BUFFER, 0); instanceBuffer->EndConfigure();
mesh.Unbind(); mesh.Unbind();
} }