OpenGL FAQ

Over the last several years, iDevGames has seen many great questions and discussions about all manner of Mac-related game development.

With the advent of the iPhone, there has recently been a increase in new developers posting questions here. And I think a lot of the same basic questions are asked over and over. In particular, questions about a subject near and dear to my heart-- OpenGL.

In my experience, at least 90% of these questions can be completely answered to anyone's satisfaction by simply reading the OpenGL documentation. But the official documentation is written in quite verbose and technical language, and may not be easy for new developers to pick up and digest, so it seems that many people don't bother reading it.

After reading and playing with all of this, you'll understand the fundamental operation of OpenGL. That leaves us with the other 10% of the questions, which tend to be "what's the best way to do X?" or "I'm trying to do X and it works on GPU Y but not GPU Z, why?". Usually, these questions can be answered by experience, or in many cases by simply trying several implementations and comparing them. And sometimes, we really need nice long discussions.

The "FAQ" link on this forum is broken, so in this thread, I'd like to ask iDevGamers what you want in a FAQ. What's important to you?

From my perspective, there are a couple of fundamental topics that should be clearly documented. Of course these are already covered in other FAQs, but this is how I'd order a list tuned to new developers on iDevGames:

1) How to program. This is outside of the realm of OpenGL, but it's absolutely crucial that you understand the basic concepts of C and how to write and debug a program before you try to make a game using OpenGL. So, see a "general programming" FAQ before going any further.

2) What is OpenGL. Strictly speaking, it's nothing more than a specification. In practice, it's a state-machine based 2D and 3D graphics API abstracting away the hardware details so that you don't have to write microcode specific to every GPU you want your game to run on. This leads to a discussion of how hardware-specific features are exposed via extensions, which is a whole topic by itself.

3) What can I do with OpenGL? It's a fairly low-level API, which (to grossly simplify things) lets you draw a bunch of texture-mapped triangles. It's not a scene graph or a game engine-- you build those things on top of OpenGL. For a new platform like the iPhone, I think it's best to understand what the hardware and API capabilities are, and design your game around that-- not the other way around.

4) What's the deal with OpenGL ES? Embedded devices like the iPhone support a small subset of the full OpenGL API; many older and "convenience" functions have been removed. The two API are generally compatible, so you can write an application for both the Mac and the iPhone if you're careful about the functions you use. This leads to a discussion of the history of the GL API and the widely differing capabilities of the various GPUs out there.

A series of "how do I?" topics, like:
5) How do I set up rendering (choosing a device, creating a context, creating a drawable.)
6) How do I draw something (vertex attributes and transformation.)
7) How do I use textures (texture objects, filtering, TexEnv.)

Check for errors. If you call any OpenGL function with invalid arguments (or try to do something impossible, like draw with an invalid framebuffer object), it will set a global error and do nothing. You'll see broken or unexpected rendering as a result.

A good general practice while you're debugging is to call glGetError in the main loop of your program. Right at the end where you swap the framebuffer is a good place. If glGetError returns non-zero, then you've used the API incorrectly somewhere. The global error is sticky, so it's not necessarily caused by the most recent GL call, it could have been from many calls ago. You can sprinkle glGetError checks around your functions to narrow it down to the exact call, or you can use OpenGL Profiler's "Break on Error" checkbox to find the exact call immediately.

Note that for performance reasons, you should only call glGetError in your Debug build. After your app is tested and debugged on all hardware configurations you need to support, turn the error checking off in your Release build.

Introspect the current state. An OpenGL context is a large collection of state, and bugs can often be caused by setting the state one way, and then forgetting to reset it later. All of the state in desktop OpenGL (and most of the state in OpenGL ES) can be be queried at any time, so you can verify it is as you expect. Again, this should only be done in your Debug build, not in your Release build.

As a simple example, let's say you're trying to draw a 3D scene and the objects overlap incorrectly, or when you rotate an object the back side of it shows up instead of the front side. This sounds like a problem with depth testing, so double check the state that could interact. For example, glIsEnabled(GL_DEPTH_TEST), glGetInteger(GL_DEPTH_FUNC, ...), glGetInteger(GL_DEPTH_BITS, ...) will show the most likely problems. A complete list of all the context state you can query is given in the state table near the end of the specification for the relevant GL version.

Another example of a common state problem is drawing while vertex array state has been left set up incorrectly. You'll usually crash if you do this, when OpenGL tries to dereference vertex data from the invalid pointers you gave it, but sometimes you'll see corrupt geometry or other strange problems. The same technique applies-- query all of the vertex array state (pointers, stride, type, client enables, buffer object bindings, etc) to make sure it's what you expect. You can make a "ValidateState" debug function, and use it before every draw.

OpenGL Profiler can help you debug state problems by showing all of the context state at any breakpoint, including convenient checkboxes to highlight the state that has changed from the default, or from the previous time you hit a breakpoint.

Break your problem down step by step. Typical scenes have many objects, and perhaps many passes to draw everything. Each pass usually needs its own state setup. Programmatically, you can simply comment out objects or passes in your code to verify which ones work and where the problem starts. After locating the first incorrect rendering, inspect the state and how your app created the input data to OpenGL (matrices, textures, vertex data, etc.)

You can also use OpenGL Profiler to run your application and watch your scene being drawn one step at a time. In Profiler, set a breakpoint after every GL call that you use to draw (glEnd, glDrawArrays, glDrawElements, etc). Then view the back buffer (and optionally depth, stencil, alpha buffers.) When you run, Profiler will break after each draw, and you can visualize the scene being built up. At each step, you can inspect all of the state to understand the inputs GL used to process the drawing.

I think it's very useful. I came from a direct x background so although the concepts are similar there are some differences. Although I had to read info on several different websites. This info could be useful to people i.e. Left Handed Coordinate system vs Right Handed, and the different views open gl maintains.

For myself a big interest would be a section on optimizations. I admit I haven't profiled much OpenGL code and at this time am unsure what operations are costly.

Other things that might be worth mentioning are specific ways to speed up rendering. For example, what are the pros and cons of: instancing, storing a lot of (unrelated) vertex data in one VBO opposed to several separate ones, indices vs normal vertex arrays, interleaved data vs a vertex array for each, ect...

And since the trend in graphics programming is all shader-based, it'd be nice to have FAQ of what should be avoided and what shouldn't be if you're preparing to go that direction, and general "sub-FAQ" for shader programming in general.