Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I cache OpenGL state such as currently bound buffers, or does OpenGL do that anyway?

Tags:

c++

opengl

A typical OpenGL call might look like the following:

GLuint buffer;
glGenBuffers(1, &buffer);
glBindBuffer(GL_SOME_BUFFER, buffer);
...

I've read that binding of buffers and other similar functions can be quite expensive. Is it worth saving the currently bound buffer, and checking it before I bind? Such as this:

void StateManager::bindBuffer(GLenum bufType, GLuint bufID) {
    if (this->m_currentBuffer[bufType] != bufID) {
        glBindBuffer(bufType, bufID);
        this->m_currentBuffer[bufType] = bufID;
    }
}

The idea behind this being that if bufID is already bound then the expensive call to glBindBuffer is missed. Is this a worthwhile approach? I assumed that OpenGL would likely implement such an optimization already, but I have seen this pattern used in a few projects now, so I am having my doubts. I am simply interested because it would be a pretty simple thing to implement, but if it doesn't make much/any difference then I will skip it (avoiding premature optimization).

like image 380
64_ Avatar asked Feb 07 '23 09:02

64_


2 Answers

This is highly platform and vendor dependent.

You're asking if "OpenGL would implement...". As you certainly understand already, OpenGL is an API specification. There are many different implementations, and whether they check for redundant state changes is entirely an implementation decision, which can (and will) be different from implementation to implementation.

You shouldn't even expect that a given implementation handles this the same for all pieces of state.

Since this topic is somewhat close to my heart based on past experience, I was tempted to write a small essay, including a few rants. But I decided that it wouldn't belong here, so here is just a list of considerations that could affect if a given OpenGL implementation tests for redundant state changes in specific cases:

  • How expensive is it to actually change the state? If it's very cheap, checking for redundant changes might simply not be worth it.
  • How expensive is it to check for redundant changes? Normally not much, but we're looking at pieces of software where every little bit counts.
  • Are important apps/benchmarks redundantly changing this state on a frequent basis?
  • What's the philosophy on responsibilities of apps vs. responsibilities of OpenGL implementations?

And yes, this is unfortunate for everybody. For you as an app writer who wants to get ideal performance across vendors/platforms, there's really no easy solution. If you add checks to your code, they will be useless, and add extra overhead, on platforms that have the same checks in the OpenGL implementation. If you do not have checks in your code, and cannot easily avoid having these redundant state changes in the first place, you may leave performance on the table on platforms where the OpenGL implementation does not check.

like image 188
Reto Koradi Avatar answered Feb 24 '23 11:02

Reto Koradi


The reason why state caching is a bad idea is simple: you're doing it wrong. You'll always be in danger of doing it wrong.

Oh sure, you corrected the mistake I pointed out, that different buffer bindings have different state. And maybe you're using a hash-table that makes lookup pretty quick, even if a new extension comes out that adds a new buffer binding point that didn't exist when you wrote your cache.

But that's merely the tip of the iceberg as far as object binding idiosyncrasies.

For example, did you realize that GL_ELEMENT_ARRAY_BUFFER is not actually context state? It's really VAO state, and every time you bind a new VAO, that buffer binding changes. So your VAO cache now has to change the shadowed element buffer binding too.

Also, were you aware that deleting an object automatically unbinds it from any context binding points it is currently bound to? And this is true even for objects that are attached to another object that is bound to the context; the deleted object is automatically detached.

Except that this is only true for certain object types. And even then, it's only true for the context that was current when the object was deleted. Other contexts will be unaffected.

My point is this: proper caching of state is really hard. And if you get it wrong, you will create a multitude of very subtle bugs in your application. Whereas if you just let OpenGL do its thing and structure your code so that multiple binding simply doesn't happen, then you don't have a problem.

like image 28
Nicol Bolas Avatar answered Feb 24 '23 12:02

Nicol Bolas