PDA

View Full Version : WARNING: glGetError is VERY expensive



knackered
09-19-2002, 11:49 AM
Just in case there are people who, like me, assumed that glGetError simply checked some kind of static flag...it obviously doesn't, because after sprinkling generous numbers of these calls throughout my renderer, I discovered (with the help of vtune) that it is actually a hugely expensive thing to do.
(obviously I only do it in debug mode, but it's so severe that debug became almost unusuable!).
Consider yourselves warned. http://www.opengl.org/discussion_boards/ubb/wink.gif
I'm curious as to why it's so costly, though - any thoughts, anyone?

no-one
09-19-2002, 12:00 PM
probably because its like a lot of the glGet*() function calls, the error state is probably held on the server side.

knackered
09-19-2002, 12:02 PM
Mmm, but you would have thought that invalid enums etc. would be dealt with in the driver.
No, I think I know why - it's because it must cause a flush of all pending gl commands, in order to determine if any have invalid arguments, etc. Is this correct?

mcraighead
09-19-2002, 01:25 PM
I'd be surprised if GetError was expensive on our implementation. Our GetError implementation could hardly be any more trivial.

- Matt

*Aaron*
09-19-2002, 01:30 PM
Now I'm worried. Knackered, what system are you running this on?

knackered
09-19-2002, 01:51 PM
It's independent of system, but all systems I've tried it on have geforce3/4's in them.
Basically, I had a function that called glGetError(), then glDrawElements(), then glGetError() again, which was being called many hundreds of times, and glGetError was singled out by vtune as being the major hotspot - even above the glDrawElements call. and removing the glGetErrors gave a 4X frame rate increase.
Watch out for those glGetErrors!

[This message has been edited by knackered (edited 09-19-2002).]

evanGLizr
09-19-2002, 02:53 PM
Originally posted by knackered:
It's independent of system, but all systems I've tried it on have geforce3/4's in them.
Basically, I had a function that called glGetError(), then glDrawElements(), then glGetError() again, which was being called many hundreds of times, and glGetError was singled out by vtune as being the major hotspot - even above the glDrawElements call. and removing the glGetErrors gave a 4X frame rate increase.
Watch out for those glGetErrors!

This is how glGetError is implemented in Mesa



/*
* Execute a glGetError command
*/
GLenum
_mesa_GetError( void )
{
GET_CURRENT_CONTEXT(ctx);
GLenum e = ctx->ErrorValue;
ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, 0);

if (MESA_VERBOSE & VERBOSE_API)
_mesa_debug(ctx, "glGetError <-- %s\n", _mesa_lookup_enum_by_nr(e));

ctx->ErrorValue = (GLenum) GL_NO_ERROR;
return e;
}


I doubt commercial implementations are more complex than this one.
If you want to get really sure, you could debug the assembly version of your implementation, I'm sure it won't be more than a few instruciones in assembler and a return.

Another thing is that even the most trivial function can appear in VTune, if called enough times (if you are CPU bound, that is).

V-man
09-19-2002, 06:12 PM
Originally posted by knackered:
Mmm, but you would have thought that invalid enums etc. would be dealt with in the driver.
No, I think I know why - it's because it must cause a flush of all pending gl commands, in order to determine if any have invalid arguments, etc. Is this correct?


Any function call goes immediatly to the driver where an error may be raised or not. Flushing or finishing should not be necessary ...

What about the vertex programs? Do they involve a server side check?

Wasn't it you that said that this function can cause a large hit.

Whoaaa baby! dejavue!

V-man

zed
09-19-2002, 06:32 PM
perhaps youre doing something like

printf("%s\n", gluString( glGetError() ) );
or whatever the correct syntax is,
true i can see that this line will be relatively slow

solarforge
09-19-2002, 06:37 PM
Originally posted by knackered:
It's independent of system, but all systems I've tried it on have geforce3/4's in them.
Basically, I had a function that called glGetError(), then glDrawElements(), then glGetError() again, which was being called many hundreds of times, and glGetError was singled out by vtune as being the major hotspot - even above the glDrawElements call. and removing the glGetErrors gave a 4X frame rate increase.
Watch out for those glGetErrors!

[This message has been edited by knackered (edited 09-19-2002).]

I have literally dozens of glGetError() calls spread throughtout my code (in particular, I have about 6 in my texture layer setup code, that is called each time a texture layer is prepared, up to four layers per material, several dozens of materials for some models) and yes, while it does have an effect, it shouldn't bother you TOO much.

4x the effect seems harsh -- especially if you were only getting 10FPS to start with... :)

But, as you should only be using it in debug it shouldn't effect your release at all, and if your only using it in debug, you should probably look at scrapping some of the unneccesary calls once you know something works...

Leathal.

knackered
09-20-2002, 12:02 AM
This is all very well and good, but removing the glGetError's gave an dramatic speed increase. I can't say much more than that, sorry.

gking
09-20-2002, 12:23 AM
Regardless of implementation, you are essentially dereferencing a function pointer every time you call glGetError. If you have enough of them, just the cost of calling and returning could be noticeable (especially with the added sanity checks in debug builds).

Function call overhead is a major reason why glDrawElements on a triangle list (in system memory) is so much faster than calling glVertex3f hundreds of times.

knackered
09-20-2002, 12:44 AM
Originally posted by zed:
perhaps youre doing something like

printf("%s\n", gluString( glGetError() ) );
or whatever the correct syntax is,
true i can see that this line will be relatively slow

Missed that one - don't be daft! http://www.opengl.org/discussion_boards/ubb/smile.gif
This is my code:



void GLContext::GLError()
{
static GLenum error=0;

error = glGetError();

if (error)
{
char string[4096];
sprintf(string, "GLError: ");
switch(error)
{
case GL_INVALID_ENUM: strcat(string, "GL_INVALID_ENUM");break;
case GL_INVALID_VALUE: strcat(string, "GL_INVALID_VALUE");break;
case GL_INVALID_OPERATION: strcat(string, "GL_INVALID_OPERATION");break;
case GL_STACK_OVERFLOW: strcat(string, "GL_STACK_OVERFLOW");break;
case GL_STACK_UNDERFLOW : strcat(string, "GL_STACK_UNDERFLOW ");break;
case GL_OUT_OF_MEMORY: strcat(string, "GL_OUT_OF_MEMORY");break;
default: strcat(string, "Unknown error");break;
}

MYASSERT(0, string);
}
}

Humus
09-20-2002, 01:42 AM
Completely irrelevant remark ...

Why

static GLenum error=0;
error = glGetError();

instead of

GLenum error = glGetError();

Can't see the purpose of using a static variable. Will reduce performance slightly, probably not measurable though.

vincoof
09-20-2002, 02:03 AM
As a static variable, error doesn't have to be allocated everytime GLError() is called.

knackered
09-20-2002, 02:04 AM
Oh, that was left in because a previous bit of code in there wanted to know what the previous error was...thanks for pointing it out humus.

knackered
09-20-2002, 02:06 AM
Originally posted by vincoof:
As a static variable, error doesn't have to be allocated everytime GLError() is called.

Yes, but it also prevents 'error' from being in a register - it will have to do a memory lookup to find the value every time...it's a mistake that it's still there.

*Aaron*
09-20-2002, 05:41 AM
Regardless of implementation, you are essentially dereferencing a function pointer every time you call glGetError. If you have enough of them, just the cost of calling and returning could be noticeable (especially with the added sanity checks in debug builds).

Come on, dereferencing a function pointer isn't _that_ expensive. On today's processors, you can call millions of functions in 1/30th of a second. I don't think he's calling glGetError a million times per frame.


Function call overhead is a major reason why glDrawElements on a triangle list (in system memory) is so much faster than calling glVertex3f hundreds of times. If you're only drawing a few hundred triangles, the glVertex3f method is not slow. It's when you start to draw thousands that it gets to be a problem. And unlike glGetError, glVertex3f takes three arguments. If Knack were calling glGetError every time he drew a triangle, I would suspect function call overhead.

I think the only people who tell us why it is slow are the folks who wrote the drivers.

Mezz
09-20-2002, 07:51 AM
Haveyou considered doing some (possibly pointless) testing as to where/why the function is slow?

I mean, it wouldn't take much time to write a dummy version of glGetError() that did nothing (or something small and silly, to try and stop the compiler optimising it away), and make a function pointer to it. Then put that in place of your glGetError() call and see if its still slow, then you'd know if it's function overhead, or if it's something slowing down inside the glGetError() function. You can also add random stuff to your dummy function to semi-mimic the true glGetError() routine and see how slow it gets. I dunno, just a thought.

-Mezz

ToolChest
09-20-2002, 09:07 AM
Just curious; did vtune pick out glGetError or your GLError? Also how many times does GLError get called a second (approximately).

knackered
09-20-2002, 09:47 AM
Unfortunately I haven't the time to redo all the work I've undone - so I'll leave the glGetError benchmarking as an exercise for the reader. http://www.opengl.org/discussion_boards/ubb/smile.gif