OpenGL – Managing Global State in Graphics Design

designglobalsgraphicsopengl

It is generally understood in software engineering that global state is bad. However, OpenGL has been designed very much embracing the concept of global state. many of the things you modify will affect everything in your program (active shading program, uniforms, active buffer, glDepthTest…)

Because of this, I ended up developing a philosophy of "do not fight the global state, embrace it". When I design OpenGL programs, I do so assuming that each function and object must set the state immediately prior to being rendered, and that nothing can be assumed prior to issuing a render command. if you want a specific depth test, set it, if you want a specific shading program, set it, if you want a given uniform value, set it. Obviously this is mostly a heuristic as setting everything every time on any function would be a horrible design.

I however find myself working with some developers that are fully against global state (for good reasons). Their design revolves around defining a small set of objects as singletons and then passing values down as parameters as much as possible. The end result is that sometimes there's 5 or more function calls between the intention (e.g "rendering") and the action (e.g calling glDrawArrays).

For example sometimes it seems, that the best place to me, in terms of easiness, clarity and refactorability, to perform an action, would violate this principle of avoiding global state by calling a function of a global object to change the value of a variable. Rather than trying to pass that variable down, which has lead to some discussions about the best design.

The question is, is it a good idea to folow the general heuristic and avoid global state as much as possible when working with OpenGL, or is this an exception to the rule and due to its design it can be considered that embracing the global state may be better in this one instance?

Best Answer

OpenGL didn't 'embrace' global state so much as it was one of few sensible solutions to a technical problem. It's expensive to communicate with a video card. Flipping bits in video memory is considerably more expensive than flipping bits in ordinary memory. As an example it's so significant that later versions of OpenGL got rid of immediate mode, because setting up buffers (i.e. retained mode), then rendering from those buffers was so dramatically more efficient it was worth the more complicated setup. It would be impractical to "pass" the entire configuration of the rendering system on every frame because of how much communication would have to take place.

In contrast, in many web server situations, it's not uncommon to pass around a massively complex data structure, encompassing the entire state of the request and whatnot, because all you're really actually passing is an 8 byte pointer.

So, the problem with global state is it can be hard to know who's modifying it and when, and so it can be tricky to reason what it's state is. So, the thing to do is to setup your application to only modify that state in well defined ways, with well defined processes, so it is easy to reason what is happening.

Just compare the rendering state with e.g. a database driven application. In many respects, the database is a big global state. However, the interface to that DB, and often the DB itself, enforces when and how it is modified, and enforces what the data looks like inside it.

One simple way you can implement something similar is to have an interface which keeps track within your application what the state of the renderer is. You know you need the renderer setup a particular way, so you can consult your interface which would know, without needing to ask the graphics card. Likewise, it can also make the decision what has to change to get the renderer into the right state in the most efficient manner.

Building 3d graphics engines is an enormously complicated and broad subject, so I can't give precise pointers or code samples. But the basic gist is you need to come up with a way to sensibly merge the global state of the 3d card with a limited scope programming model on the CPU side, rather than having your application use all global variables.

In your specific example, when you say the other devs are defining several singletons and passing them around I get highly suspicious, but without seeing more of what's going on I can't really comment on that in more detail.

Related Topic