Archived

This topic is now archived and is closed to further replies.

v_d_d

glIsEnabled() and performance question

Recommended Posts

Is it faster to call glIsEnabled() to get the state of a certain feature before calling glEnable() or glDisable()? Ex: if(glIsEnabled(GL_TEXTURE_2D)) { glDisable(GL_TEXTURE_2D); } else { // already disabled // do nothing, (just to show the logic) } // rest of code follows

Share this post


Link to post
Share on other sites
OpenGL really doesn''t like it when you try to enable something that''s already enabled. This can lead to a big performance hit. I''m not sure about glIsEnabled(), but judging by the information I have, it would make sense. The only true way to find out is to test it your self. You could also keep track of what''s been enabled or disabled yourself.

(edit: These internal server errors are getting annoying! 4 so far on just this post)

-------------------
Realm Games Company

Share this post


Link to post
Share on other sites
quote:

I think actually that querying the state machine is very, very slow, and that OpenGL doesn''t like it too much... I think the performance would drop if you were doing it this way.



I don''t agree at all. Why would querying a variable cause something to lag?
You''re not changing anything. It''s ridiculous to me.

Share this post


Link to post
Share on other sites
Any sane video driver will keep a local cache of the current GL state. How complete that state cache is, depends on the driver.

I wasn''t entirely sure how current drivers handle the issue, so I just checked it. I compiled a ''glIsEnabled(GL_TEXTURE_2D)'' command, and stepped through the nvogl driver ASM. Driver used is the newest Detonator under NT4 (GeForce 4 Ti 4600).

The driver first goes through a rather large compare/branch sequence (probably from a switch statement, checking the argument of glIsEnabled), and finally retrieves the state from a locally cached system RAM state table (indexed by the currently active texture unit). It doesn''t look like the most efficient implementation possible (keeping track of the state yourself is going to be faster), but it certainly isn''t slow, and it won''t read back from the hardware.

/ Yann

Share this post


Link to post
Share on other sites
Yann, what about enabling a state when it is already enabled? Does it do something like the glIsEnabled() before trying to enable it, or does it just go through all the process of enabling the state?

Share this post


Link to post
Share on other sites
From the NVIDIA Opengl SDK performance FAQ

quote:

39. How expensive are glEnable and glIsEnabled commands?

It depends on the enable how expensive a given enable is, but there are some general rules.

The one expensive thing about glEnable/glDisable is that they end up being quite large switch statements. The compiler makes the switch statements into a big binary decision tree. Since you (hopefully) don''t re-enable something multiple times, the branches in the binary decision tree tend to be mispredicted. This is just an unfortunate part of OpenGL''s design. At some point, NVIDIA might change our driver implementation to a perfect hash table instead of binary decision tree, but that is a low priority.

Both glEnable and glIsEnabled both have large switch statements.

In general, the glEnable/glDisable call itself is cheap since usually it just sets a "dirty" bit and returns.

At the next rendering call (glBegin, glDrawPixels, etc), the dirty bits are tested. If any are set, the driver goes through a validation process based on what bits have been set. This can be fairly expensive.

Apps can benefit from inline routines such as:

static void INLINE
myEnableLightingTest(__MYcontext *mystate)
{
if (mystate->depthTest) {
// already enabled
} else {
glEnable(GL_LIGHTING);
mystate->lighting = 1;
}
}

static void INLINE
myDisableLightingTest(__MYcontext *mystate)
{
if (mystate->lighting ) {
glDisable(GL_LIGHTING);
mystate->lighting = 0;
} else {
// already disabled
}
}

These custom routines are definitely better than using:

static void INLINE
myEnableLightingTest(void)
{
if (glIsEnabled(GL_DEPTH_TEST)) {
// already enabled
} else {
glEnable(GL_LIGHTING);
}
}

The above code has to determine two different large binary decision trees if lighting is currently disabled! Plus both glIsEnabled and glEnable both test if you are in a begin/end. Recording the last state yourself is a better approach. The other problem with determining two large binary decision trees is that this tends to overwhelm the CPU''s internal branch prediction logic and thereby increases the mispredicted branches in other code.

It may also be interesting to have another inline routine for when you definitely know that the state is being set different:

static void INLINE
myForceEnableLightingTest(__MYcontext *mystate)
{
assert(mystate->lighting == 0);
glEnable(GL_LIGHTING);
mystate->lighting = 1;
}

The same practice applies for myForceDisableLightingTest.

Because of the dirty bits, redundant glDisable/glEnable calls are not particularly expensive:

glDisable(GL_LIGHTING);
glEnable(GL_LIGHTING); // silly but not particularly expensive
glDisable(GL_LIGHTING); // silly but not particularly expensive
glBegin(...); // a single validation occurs at glBegin

Though the following code is very expensive:

glEnable(GL_LIGHTING);
glDrawArrays(...);
glEnable(GL_LIGHTING); // forces a useless validation
glDrawArrays(...);

This advice generally applies to all OpenGL state.

As a rule, the driver does not "second guess" any OpenGL calls that you make to check if the state is already being set as the state is currently. The rationale is that applications can always "short-circuit" null state changes more efficiently than the driver.

Plus there is typically always at least one check about whether the driver is in a begin/end or not that must be performed. There is also often a complex switch statement that must be decided (as in the glEnable/glDisable case).

Note that the begin/end check cannot be short-circuited by the driver in advance of checking if the state is being set to the existing state.

Consider:

glEnable(GL_LIGHTING);
glBegin(...);
glEnable(GL_LIGHTING); // even though redundant, should generate an error


Share this post


Link to post
Share on other sites