Optimizing several glDrawElements calls
Posted: Sat Aug 13, 2011 3:10 pm
I am currently working on the font system for my game's engine, and I've come up with this great design that gives me great frame rates and a shitload of flexibility. However, there is some optimization that I think could be done with my rendering code, and I've come up against a bit of a snag in getting it to work. The original code works (the #if 0 code), but the "optimized" code does not!
DISCLAIMER: This is the first time I've really used vertex buffer objects, so I am probably missing something crucial. I've checked the documentation for the relevant functions several times and can't seem to find anything.
So here's how it works:
I've got a FontRenderable class which generates several VBOs when it's constructed. Each of these VBOs corresponds to a different texture (each font face in my engine is a single texture, and this class can render using multiple faces, styles, colors, etc.), with the exception of an optional VBO that's generated when a part of the text given to the FontRenderable constructor is flagged to be underlined. This VBO does not use texture coordinates, because it's a solid color line.
These VBOs are then rendered when the FontRenderable object's Render() method is called. My current working version of the function involves a lot of (what I percieve to be) unnecessary state changes. It enables/disables the client states for each VBO, sets the vertex/color/texture pointers each iteration, enables/disables the GL_TEXTURE_2D state, etc. This seems like a lot of stuff that doesn't need to be done for every single VBO. What I would like to do is set this stuff beforehand, do a quick loop over my VBOs, render them, and then disable texturing and render the underline VBO separately. Basically, I want to take as much out of the for loop as possible. This might not even be possible for all I know--glDrawElements() might clobber the fuck out of the client states, or maybe you can't call glEnableClientState() or gl*Pointer() before glBindBuffer(). I don't know.
Code is below (I know it looks long; it's because there are two different versions of the function)
Edit: I forgot to actually mention what doesn't work--online the underline VBO is rendered.
DISCLAIMER: This is the first time I've really used vertex buffer objects, so I am probably missing something crucial. I've checked the documentation for the relevant functions several times and can't seem to find anything.
So here's how it works:
I've got a FontRenderable class which generates several VBOs when it's constructed. Each of these VBOs corresponds to a different texture (each font face in my engine is a single texture, and this class can render using multiple faces, styles, colors, etc.), with the exception of an optional VBO that's generated when a part of the text given to the FontRenderable constructor is flagged to be underlined. This VBO does not use texture coordinates, because it's a solid color line.
These VBOs are then rendered when the FontRenderable object's Render() method is called. My current working version of the function involves a lot of (what I percieve to be) unnecessary state changes. It enables/disables the client states for each VBO, sets the vertex/color/texture pointers each iteration, enables/disables the GL_TEXTURE_2D state, etc. This seems like a lot of stuff that doesn't need to be done for every single VBO. What I would like to do is set this stuff beforehand, do a quick loop over my VBOs, render them, and then disable texturing and render the underline VBO separately. Basically, I want to take as much out of the for loop as possible. This might not even be possible for all I know--glDrawElements() might clobber the fuck out of the client states, or maybe you can't call glEnableClientState() or gl*Pointer() before glBindBuffer(). I don't know.
Code is below (I know it looks long; it's because there are two different versions of the function)
Code: Select all
void FontRenderable::Render()
{
#if 0
// Draw the vertex buffers.
std::map<std::string, GLuint>::const_iterator iterator;
for (iterator = mHandles.begin()
; iterator != mHandles.end()
; ++iterator)
{
glDisable(GL_TEXTURE_2D);
glBindBuffer(GL_ARRAY_BUFFER, iterator->second);
// Configure the vertex state.
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_FLOAT, sizeof Vertex, reinterpret_cast<GLvoid*>(0));
// Configure the color state.
glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof Vertex, reinterpret_cast<GLvoid*>(8));
// Configure the texture state if this is not the underline buffer.
if (iterator->first != "__underline__")
{
glEnable(GL_TEXTURE_2D);
mFamily.Face(iterator->first).Texture().Active(true);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, sizeof Vertex, reinterpret_cast<GLvoid*>(12));
}
glDrawElements(GL_QUADS, mIndices[iterator->first].size(), GL_UNSIGNED_INT, &mIndices[iterator->first][0]);
// Disable the states.
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
}
glEnable(GL_TEXTURE_2D);
#else
// Configure the vertex state.
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_FLOAT, sizeof Vertex, reinterpret_cast<GLvoid*>(0));
// Configure the color state.
glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof Vertex, reinterpret_cast<GLvoid*>(8));
// Configure the texture state.
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, sizeof Vertex, reinterpret_cast<GLvoid*>(12));
// Draw the text buffers.
std::map<std::string, GLuint>::const_iterator iterator;
for (iterator = mHandles.begin()
; iterator != mHandles.end()
; ++iterator)
{
// The underline buffer is drawn separately.
if (iterator->first == "__underline__")
{
continue;
}
mFamily.Face(iterator->first).Texture().Active(true);
glBindBuffer(GL_ARRAY_BUFFER, iterator->second);
glDrawElements(GL_QUADS, mIndices[iterator->first].size(), GL_UNSIGNED_INT, &mIndices[iterator->first][0]);
}
// Disable the texture state.
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisable(GL_TEXTURE_2D);
// Draw the underline buffer.
iterator = mHandles.find("__underline__");
if (iterator != mHandles.end())
{
glBindBuffer(GL_ARRAY_BUFFER, iterator->second);
glDrawElements(GL_QUADS, mIndices[iterator->first].size(), GL_UNSIGNED_INT, &mIndices[iterator->first][0]);
}
// Disable the color and vertex states.
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
#endif
}