How to render this more efficiently?

Hey all, so with my current method I can barely hit 10,000 triangles without my computer slowing down and was wondering how to speed it up. I have a sprite class which has a render method that renders a triangles to the screen. Here’s that code:

Sprite::Sprite(){


    
    GLfloat triangle_vertices[] = {
        0.0f,  0.8f,
        -0.8f, -0.8f,
        0.8f, -0.8f,
    };
    
    //Buffer for triangle vertices
    glGenBuffers(1, &vbo_triangle);
    glBindBuffer(GL_ARRAY_BUFFER, vbo_triangle);
    glBufferData(GL_ARRAY_BUFFER, sizeof(triangle_vertices), triangle_vertices, GL_STATIC_DRAW);
    
                        
    const char* attribute_name = "coord2d";
    attribute_coord2d = glGetAttribLocation(program, attribute_name);
    if (attribute_coord2d == -1) {
        fprintf(stderr, "Could not bind attribute %s
", attribute_name);
    }
}


void Sprite::render(){
    
    
    glEnableVertexAttribArray(attribute_coord2d);
    
    glBindBuffer(GL_ARRAY_BUFFER, vbo_triangle);
    glVertexAttribPointer(
                          attribute_coord2d, 
                          3,                 
                          GL_FLOAT,     
                          GL_FALSE,          
                          0,                 
                          0          
                          );
                          
    glDrawArrays(GL_TRIANGLES, 0, 3);
    glEnableVertexAttribArray(0);
    
    
}

So as you can see, my constructor creates and binds buffer data, and every time I call render it draws them to the screen, and I call it like this:

void onDisplay()
{
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT);
    
    glUseProgram(program);
    
    for(spriteIterator = spriteVec.begin(); spriteIterator != spriteVec.end(); ++spriteIterator){
        (*spriteIterator)->render();
    }
    
  
    glutSwapBuffers();


}


void idle()
{


    glutPostRedisplay();
}

What ways can I improve the efficiency of this code? (Eventually the vertices will be dynamic, the single triangle is there for example) Thanks!

The major speed factor here is probably that you only draw one triangle for every call to glDrawArrays().

Some other comments:

  • glEnableVertexAttribArray() takes a vertex attribute array as argument (not an attribute location). These are created by glGenVertexArrays(). I am not sure, but I suppose using the attribute_coord2d will be used as a VAO, but you may get into problems if you have more VAOs. The VAO is not a container of vertex data or similar things, it is more like a container where pointers and states are stored. They are mandatory to use from OpenGL 3 I think.
  • glVertexAttribPointer() is only needed once, in the initialization. The state will be stored in the VAO.
  • It is generally a good practice to use the constructor of a class for initializing the class to a “zero” state. Then you use an Init() function to set it up. That makes it easier to handle errors, maybe returning a bool. It also removes lot of problems you get into if you would have a global instance of such a class.
  • The call to glBindBuffer(GL_ARRAY_BUFFER, vbo_triangle) is not saved in itself in the VAO, but the VBO reference defined by glVertexAttribPointer() will saved in the VAO. That means glBindBuffer(GL_ARRAY_BUFFER) is also only needed in the initialization.

So you have 10k vertex buffers with the same data? That looks bad.
Doing single triangle per batch doesnt seem all that good either.

You could look at using a texture for the sprite locations and uses instance rendering and lookup the location in the vertex shader using the instance (gl_InstanceID). This way you only need one call to such as glDrawArraysInstanced. To move the sprites you update the texture buffer. If they all move you may use 2 texture buffers and toggle between frames.

So in the tutorials I was learning from they never explicitly mentioned VAOs, after doing some research I’m confused as to what exactly they do. Do I have to create them specifically? What are their benefits?

Also a quick question about glEnableVertxAttribArray() this allows me to add multiple attributes to the same buffer right?

So I can do something like this?


glEnableVertexAttribArray(0);
glVertexAttribPointer(some data);
glEnableVertexAttribArray(1)
glVertexAttribPointer(some other data);

Also would you recommend I render everything with one glDrawArrays() call?

VAO came into the standards in the last couple of versions; at first they were optional now you require one to manage your buffer. They remember all the buffer bindings and attributes; so you bind it instead of redoing all the binding when you want to draw. Technically you only need one and bind it before all your current bind code; but usually you have one per buffer.

Here is a code snip


    if (VAO_ID == -1)
    {  // once off init code, can be anywhere
      Vector2F vertex[4];
      vertex[0].x = -1; vertex[0].y = -1;
      vertex[1].x =  1; vertex[1].y = -1;
      vertex[2].x = -1; vertex[2].y =  1;
      vertex[3].x =  1; vertex[3].y =  1;

      glGenVertexArrays(1, &VAO_ID);
      glBindVertexArray(VAO_ID);
      glGenBuffers(1, &VBO_ID);
      glBindBuffer(GL_ARRAY_BUFFER, VBO_ID);
      glBufferData(GL_ARRAY_BUFFER,4*sizeof(Vector2F),vertex,GL_STATIC_DRAW);
      glVertexAttribPointer(VERTEX_BINDINGS_POSITION, 2, GL_FLOAT, GL_FALSE, 0, 0);
      glEnableVertexAttribArray(VERTEX_BINDINGS_POSITION);
    }
    else
    { // done before your render of the buffer
      glBindVertexArray(VAO_ID);
    }

sorry,
for the second part of your question; yes you set all the sttributes at once. My code does need glDrawArrays so I am unsure if it speed things; from what I have read it provides little speed gain but I am willing to stand corrected on that.

http://www.opengl.org/wiki/Vao
http://www.opengl.org/wiki/Tutorial1:_Rendering_shapes_with_glDrawRangeElements,_VAO,_VBO,_shaders_(C%2B%2B_/_freeGLUT)

Hm, I’m using a mac with OpenGL 2.1, and this says it wasn’t adopted until 3.0.

That’s just when it was pulled into the core OpenGL spec.

You may still have it. See if you have the the ARB_vertex_array_object extension in your OpenGL driver. On Linux, you’d do this via:

glxinfo | grep ARB_vertex_array_object

It didn’t return anything, though ARB_vertex_buffer_object did, I don’t know if that’s relavent.

Vertex buffer objects (VBOs) are different entities than Vertex array objects (VAOs). VBOs allow you to store batch data in driver memory rather than application memory, whereas VAOs capture the bindings end enables used for submitting batch data.