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)
    std::vector<glm::mat4> models; // collect all model matrices
    std::glm::vec4> colors;        // collect all colors

    for( activeParticle : emitter.activeParticles())

    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

    // 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);
    // Fill mesh buffer
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(particle_quad), particle_quad, GL_STATIC_DRAW);
    // Set mesh attributes
    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?


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

  • 1
    \$\begingroup\$ This should be a lesson to always benchmark before optimizing. \$\endgroup\$
    – Omegastick
    Commented Jun 18, 2019 at 9:24

1 Answer 1


Found my mistake at the initialization of the color buffer.

instead of

 GLsizei datasize = sizeof(GLfloat);

it should be

GLsizei datasize = sizeof(glm::vec4);
  • 2
    \$\begingroup\$ You might want to mark the answer as accepted to show users that it fixed the issue :) \$\endgroup\$
    – Vaillancourt
    Commented Jul 18, 2019 at 13:06

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .