feat: instance + array buffer
This commit is contained in:
@ -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);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,15 +17,16 @@ namespace OpenGL {
|
|||||||
public:
|
public:
|
||||||
Buffer(BufferTarget target, BufferUsage usage);
|
Buffer(BufferTarget target, BufferUsage usage);
|
||||||
|
|
||||||
|
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 +37,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 +48,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
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,11 +13,11 @@ namespace OpenGL {
|
|||||||
Unbind();
|
Unbind();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Buffer::Bind() {
|
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 +33,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 +55,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
|
||||||
@ -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());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -397,11 +397,11 @@ 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
|
||||||
@ -409,7 +409,7 @@ void Object::EnableBatch(unsigned int instanceVBO) {
|
|||||||
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();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user