0
\$\begingroup\$

I'm looking to render rectangle primitives as performant as possible, here's what I did so far:

int main() {
    Window window("OpenGL", 800, 600);

    Shader shader("./file.shader");

    float vertices[8]; 
    glGenVertices(vertices, 200, 150, 400, 300); // return coords for a square
    const GLshort indices[6] = { 0, 1, 2, 2, 3, 0 };

    GLuint vao, vbo, ebo;
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);

    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);

    glGenBuffers(1, &ebo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    while(window.isOpen()) {
        window.clear();

        shader.use();
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, NULL);
        shader.setUniforms(..);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, NULL);

        window.update();
    }

    glDeleteBuffers(1, &vbo);
    glDeleteBuffers(1, &ebo);
    glDeleteVertexArrays(1, &vao);

    return 0;
}

So far what I'm doing to fix my problem and seems to work:

  1. Created vao, vbo and ebo to make a basic indexed rectangle.

  2. Made a function that will returns vertices coordinates based position and sizes(x, y, w, h)

  3. Create one single shader and set uniforms like position offsets, sizes and eventualy colors/textures after every draw call to be able to render shapes in different places ( that makes the coordinates function useless)

I'm pretty sure that's not the way to do it, but can't find something else, so this is what I'm thinking.

  1. Heard that switching shaders is expensive, but I'll require shapes that won't share the same shader..

  2. Also heard that setting uniforms is slower than switching shaders, so probalby switching shaders will be better here, need to profile the code to see.

  3. Currently drawing one by one with glDrawElements seems hardcoded and inefficient to me, need something like a vao that could store multiple rectangular like indexedvbo all preloaded before drawing and when it goes to drawing, draw all the data with one call.

  4. Not sure if I can load shaders between drawing, some of them will require changing while drawing like a hover or click effect, maybe there's better practices here too.

At the end, if you've been through this issue or having some tips/docs/source codes to read, it'll be excellent, thanks!

BIG NOTE: Mostly looking for 2d support, no 3d needed in case this could make it harder.

\$\endgroup\$
  • 1
    \$\begingroup\$ Why do you need this good of a performance for 2d? Are you going to render hundreds of thousands of rectangles?Try to avoid premature optimization. \$\endgroup\$ – Bálint Dec 20 '17 at 18:17
  • \$\begingroup\$ As alway, premature optimization is the root of all evils, but I'm also trying to learn to do stuff the good way. I'm working on a gui library, so I guess that performance is not that important in this case \$\endgroup\$ – alex1997 Dec 20 '17 at 21:11
  • \$\begingroup\$ The good way is the convenient way. If you need to go out of your way to make the engine render a complex UI with 4000 frames a second instead of 3500, then that's arguably not the good way. \$\endgroup\$ – Bálint Dec 20 '17 at 22:37
1
\$\begingroup\$

So, if I understand your question, correctly, you are trying to draw multiple rectangle objects as efficiently as possible... like sprites?

You are on to the right idea when you think about how to best optimize calls to openGL. The bottleneck in openGl is almost always with calling objects with your CPU faster than it can transmit them across the bridge to the GPU. There are a few tools that openGL comes with to facilitate resolving this issue.

You can use SubBufferData to inject data into your VBO at specified places. This works well if you create a larger buffer and then add multiple objects to it.

You can map your Buffer, so that you can inject new data into it. With this method, you update the contents of the buffer. This method is similar to SubBufferData, and depending on hardware, one or the other might have a better performance.

There is Index Drawing, if multiple objects use the same vertex data, you can submit them and GLSL has a function to let you derive an index from each set of data. You can then operate based on the index value in the shader to differentiate the results.

There is indirect calling. I'm less familiar with this one, but from my understanding is that you create a storage that is then manipulated by a shader before being stored again to be used by OpenGL again later.

There might be more that I'm not aware of, however, if it was me, and you're drawing similar rectangles, i'd use Index drawing, supplying the TexCoords and Position to the shader, referencing it's Index. You can then use a Sprite sheet texture and one VBO to draw your whole scene.

\$\endgroup\$
0
\$\begingroup\$

Any OpenGL state change does indeed carry a cost, but like all things, your algorithm is what will decide performance, not small optimisations.

That being said: If you are going to draw many quads (which, to OpenGL is actually two triangles), and by many, I mean thousands+, you can use a technique called instanced rendering. The idea goes like this:

  1. Enable shader of choice
  2. bind textures
  3. compute transforms for each quad and store in a contiguous array.
  4. Bind the array data to a uniform buffer object (UBO)
  5. Use the buffer.
  6. Send any additional data you need to the shader via uniforms
  7. Call one of the following:

void glDrawArraysInstanced(GLenum mode​,GLint first​,GLsizei count​,GLsizei instancecount​);

void glDrawElementsInstanced(GLenum mode​,GLsizei count​,GLenum type​,const void *indices​,GLsizei instancecount​);

Then, continue your rendering as normal.

\$\endgroup\$

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.