As it turns out, OpenGL loves to check for errors after every call. This makes you spend about double the time in OpenGL every frame. We were going kinda crazy trying to figure out what the problem was; I assumed it was my fairly elaborate collision detection. But running the Python profiler exposed the truth, and now we’re at a happy 30 frames per second again.