In my 2D Game Engine I'd like to reduce the amount of draw calls. I used to do a draw call for every particle, which yields worst performance.
I've tried instanced rendering in OpenGL but my performance didn't change ( and the particles were only drawn every few frames). Every particle emitter has a Vertex Buffer for the model matrix & color of every particle. The buffers memory is preallocated with the max amount of particles. During rendering all active particles are collected and the data send to the GPU via glBufferSubData.
My instanced rendering looks like this
set projection & view matrix as uniforms
for ( emitter : particleEmitters)
glBindVertexArray(emitter.VAO)
emitter.GetTexture().Bind()
std::vector<glm::mat4> models; // collect all model matrices
std::glm::vec4> colors; // collect all colors
for( activeParticle : emitter.activeParticles())
models.push_back(activePartile.modelmatrix)
colors.push_back(activeParticle.color)
if(models.size() > 0)
// fill model matrices buffer
glBindBuffer(GL_ARRAY_BUFFER, emitter.modelVBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(glm::mat4) * models.size(), &models[0]);
// fill color buffer
glBindBuffer(GL_ARRAY_BUFFER, emitter.ColorVBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(glm::vec4) * colors.size(), &colors[0]);
glDrawArraysInstanced(GL_TRIANGLES, 0, 6, models.size());
// Unbind and other stuff...
And the initialization of the buffers
ParticleEmitter::ParticleEmitter()
{
// Set up mesh and attribute properties
GLuint VBO;
GLfloat particle_quad[] = {
// Pos // Tex
-0.5f, 0.5f, 0.0f, 1.0f,
0.5f, -0.5f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.0f, 1.0f,
0.5f, 0.5f, 1.0f, 1.0f,
0.5f, -0.5f, 1.0f, 0.0f
};
glGenVertexArrays(1, &this->m_vao);
glGenBuffers(1, &VBO);
glBindVertexArray(this->m_vao);
// Fill mesh buffer
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(particle_quad), particle_quad, GL_STATIC_DRAW);
// Set mesh attributes
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (GLvoid*)0);
// setup model matrices buffer
glGenBuffers(1, &modelVBO);
glBindBuffer(GL_ARRAY_BUFFER, modelVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::mat4) * maxParticles, nullptr, GL_DYNAMIC_DRAW); // allocate space for max amount
{
GLuint location = 2;
GLint components = 4;
GLenum type = GL_FLOAT;
GLboolean normalized = GL_FALSE;
GLsizei datasize = sizeof(glm::vec4);
char* pointer = 0;
GLuint divisor = 1;
for (int c = 0; c < 4; ++c)
{
glEnableVertexAttribArray(location + c); //location of each column
glVertexAttribPointer(location + c, components, type, normalized, datasize, pointer + c * sizeof(glm::vec4)); //tell other data
glVertexAttribDivisor(location + c, divisor); //is it instanced?
}
}
// setup color buffer
glGenBuffers(1, &ColorVBO);
glBindBuffer(GL_ARRAY_BUFFER, ColorVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec4) * maxParticles, nullptr, GL_DYNAMIC_DRAW); // allocate space for max amount
{
GLuint location = 1;
GLint components = 4;
GLenum type = GL_FLOAT;
GLboolean normalized = GL_FALSE;
GLsizei datasize = sizeof(GLfloat);
char* pointer = 0;
GLuint divisor = 1;
glEnableVertexAttribArray(location); //location of each column
glVertexAttribPointer(location, components, type, normalized, datasize, pointer); //tell other data
glVertexAttribDivisor(location, divisor); //is it instanced?
}
glBindVertexArray(0);
}
Since the particles are drawn for only ~10 frames in a 60frame cycle (but correctly) , I assume my layout is somewhat wrong.
Why are the particles not drawn every frame?
@edit: After commenting out the whole section about glBufferSubData and glDrawArraysInstanced the performance didn't change. The performance killer is in fact the calculation of the model matrix on the CPU for every active particle