# How can I debug _why_ glDrawArrays is very slow?

I'm working on a game that uses CEGUI and Ogre. Recently, we've discovered that some of our customers with GeForce4 MX 4000's, performance is terrible.

After a night spent debugging, I've tracked the issue down to a glDrawArrays call inside the Ogre GL Renderer. Most executions of this call are pretty quick (<10ms, which still sucks but is ok for this terrible card). But one execution of the GLRenderSystem::_render function takes 3500ms every single frame.

It's only drawing a 54-element TRIANGLE_LIST. All the other calls to it are drawing similar-sized TRIANGLE_LISTs, so it's not the size of the array or its type that is the problem.

I've:

• disabled fragment shaders (the GF4 MX doesn't support them)
• disabled vertex shaders (it supports them, but CEGUI can only do all types of shaders or none of them)
• attempted to disable a nearby call for multitexturing
• massively simplified the UI that I'm drawing so that it is only a single CEGUI::Window

But I'm really just shooting in the dark. I'm in a windows environment. What's a good tool to figure out the OpenGL state at the time of a function call? I'm guessing that there's a particularly piggish texture or a fragment shader somehow slipping through and getting rendered.

If there aren't any tools to do what I need, can anyone suggest ways that glDrawArrays could be made really slow on an old card?

Update:

• I grabbed GLTrace and compared the calls that precede both a fast glDrawArrays and a slow one. They're absolutely identical, they're even binding the same texture IDs.
• One consistent difference however, is that the call that executes slowly is the first one in each OGRE/cegui render frame. Perhaps there is some copying-over of texture data to the video card each frame (which would be ridiculous and clearly doesn't happen on other cards...). Update: this isn't the case - no matter where in the queue I draw the offending object, it still takes forever. See below.
• There are 3 main objects being drawn: a full-window background (plain white, gets textured with a 128x128 texture), a smaller rectangular label, and text in that label. The ratios between their draw speeds are approximately the same the ratios between their areas
• If I force the slow object to be drawn several times instead of drawing the entire render queue, it is slow every time, which means it's not a one-time-loading issue.

Update for current theory: this is somehow a fill-rate issue. Is there a way to configure a window so that the card has issues filling it? Since I'm drawing in windowed mode, will that mess up a card sometimes? My window doesn't have power-of-two dimensions (or dimensions similar to any standard resolution), could that be a chokepoint?

• Using vertex buffers of any kind? – Sean Middleditch Jun 18 '13 at 2:51
• You might try gDEBugger. – Nathan Reed Jun 18 '13 at 3:00
• That does sound like the gpu is stalling, were the textures or vertex buffers used modified in the current frame? – Archy Jun 18 '13 at 7:54
• Obviously. Do you have a specific profiler you use or can recommend for OpenGL-specific profiling? I've used very sleepy a ton, but since it can't resolve the call stack within the nVidia driver it hasn't been helpful. Plus through simple stepping-through, I know exactly which call is slow. – ArtHare Jun 18 '13 at 13:49
• @user1158478: glBufferData, glBufferSubData, and glGenBuffers are the ones to look for. You should only be calling glGenBuffers during loading and you should be using glBufferData sparingly. Remember to discard buffers before updating them if you've already used them this frame; there's no guaranteed GL way to do this in any version supported by that ancient hardware, but drivers typically allow a hack for this by using glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_DYNAMIC_DRAW) (replacing GL_ARRAY_BUFFER as appropriate). – Sean Middleditch Jun 18 '13 at 17:55

## 2 Answers

Got it:

1. The Geforce4 MX with the newest-available nVidia drivers (circa 2006) doesn't support the glTexEnv approach to blending source and destination textures. At least, not in hardware. Drawing the simplest shapes results in crippling slowness.
2. However, it does appear to support GL_BLEND combined with glBlendFunc in hardware.

Solution: Instead of the complicated cegui setup show below:

glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE_EXT)
glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB_EXT,GL_MODULATE)
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE0_RGB_EXT,GL_TEXTURE)
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE1_RGB_EXT,GL_PRIMARY_COLOR_EXT)
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE2_RGB_EXT,GL_CONSTANT_EXT)
glTexEnvi(GL_TEXTURE_ENV,GL_RGB_SCALE_EXT,GL_LINES)
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND0_RGB_EXT,GL_SRC_COLOR)
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND1_RGB_EXT,GL_SRC_COLOR)
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND2_RGB_EXT,GL_SRC_ALPHA)
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND0_ALPHA_EXT,GL_SRC_ALPHA)
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND1_ALPHA_EXT,GL_SRC_ALPHA)
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND2_ALPHA_EXT,GL_SRC_ALPHA)
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE_EXT)
glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_ALPHA_EXT,GL_MODULATE)
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE0_ALPHA_EXT,GL_TEXTURE)


I'm going to do this: glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA)

If you, future reader, are modifying your OgreGLRenderSystem as well, I'm going to do this change in GLRenderSystem::_setTextureBlendMode in OgreGLRenderSystem.cpp.

• Ouch! I knew it was bad but never realized it was that bad. I'd actually guess it more likely supports a more minimal subset of modes and something in there tripped it over. Maybe constant as source2? (That GL_LINES for RGB scale is OK as GL_LINES is defined as 1; looks like an error in whatever you used to dump the calls). – Maximus Minimus Jun 18 '13 at 23:26
• If the conditional switch to glBlendFunc works, I'm probably just going to stop there. I've thrown a lot of hours after this problem, and there are other areas I've got to work on. Notably, the card doesn't have GL_EXT_blend_func_separate, which makes me think that the separate blending of RGB and alpha that Ogre is attempting to do may not fly. – ArtHare Jun 18 '13 at 23:53
• If it works well for you there's no reason not to stop there. – Maximus Minimus Jun 20 '13 at 16:40

I got it, too. For me it was an issue with the Texture. On NVIDA things worked out fast. But my optical optimisation caused an ATI card to nealy resign.

I work with a texture of size 4720 x 5600 (about) and modern cards work well with it.

I tried to optimize borders using Wrapmode=GL_CLAMP_TO_BORDER_ARB . Therefore I need to supply the border argument to glTeximage2D as at least == 1. Since the questionable Card did not support border in HW, it turned out to take 20 sec for one frame. Going back to GL_CLAMP_TO_BORDER + border param==0 made it speed up again.