0
\$\begingroup\$

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

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

Found my mistake at the initialization of the color buffer.

instead of

 GLsizei datasize = sizeof(GLfloat);

it should be

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.