Flamingo Dev Log

Archive for June 2010

PyOpenGL too slow? Use C.

leave a comment »

Recently I’ve been doing a lot of tinkering with the rendering routines in flamingo, which are still a bit primitive. The current way to render a bitmap image is to create a Sprite (with a bitmap loaded from the image module). Then the engine loops through all of the Sprites currently “alive” and then draws them with OpenGL primitives. There are definitely faster ways to accomplish the rendering, but I wanted to start with the simplest possible thing that would work.

(This is a slimmed down picture of what the engine is doing. There are also multiple “Screens”, so there is the potential to draw a single Sprite more than once. Currently the engine just loops through all of the Sprites for each Screen. There are better ways to do this, they will be implemented in the future.)

I was using PyOpenGL 3 to draw the primitives, but it was unexceptably slow. PyOpenGL_accelerate barely helped the problem. Even with only 3 Sprites the engine was running around 80 frames per second. Adding more Sprites slowed it down to around 20. (My old laptop is partly to blame for this.)

I experimented with Display Lists. They offered only a slight increase in speed. I implemented a method to cull the Sprites that had no chance of being drawn that frame (they were outside of all of the Screens). This didn’t help the speed much because there weren’t any Sprites not being drawn in my test (tests/flamingo_test.py).

After my options were exhausted (for the most part), I decided to do the same thing I did for the fmath module: I ported the drawing functions to C. I had planned to just call the PyOpenGL functions from C, but I decided to experiment. What I found was fairly surprising (to me) and pretty awesome. I’m documenting it here for others in the same situation.

Turns out you can include the OpenGL C header files to your extension module, link the OpenGL libraries to the extension module, call the C OpenGL routines, and, (if you tell Pygame to set up a OpenGL window), you can render directly to the Pygame window. You can also mix these C OpenGL calls with PyOpenGL calls with no fuss.

(From what I understand, this works because PyOpenGL and Pygame share the same rendering context. Loading the same shared library and not changing the context will result in OpenGL rendering to the same context.)

The results: much faster rendering without any headache. Here’s how to do it.

First off, you need to tell Pygame to set up an OpenGL window, which is very easy:

pygame.display.set_mode((800, 600), pygame.constants.OPENGL | pygame.constants.DOUBLEBUF)

Then I did the initial OpenGL setup with PyOpenGL calls (it’s convenient to do it right after creating the window, and that was called from Python anyway).

Then, when you create your extension module, be sure to include the header files:

#include <gl/gl.h>
#include <gl/glu.h>    // You may not need this
#include <gl/gle.h>    // Or this

Then, just call whatever OpenGL routines you need:

double hw = 50.0;
double hh = 20.0;


glTexCoord2f(0.0, 0.0);
glVertex2f(-hw, hh);
glTexCoord2f(0.0, 1.0);
glVertex2f(-hw, -hh);
glTexCoord2f(1.0, 1.0);
glVertex2f(hw, -hh);
glTexCoord2f(1.0, 0.0);
glVertex2f(hw, hh);


And the final step is linking the actual OpenGL libraries to the extension module when you compile it. Thankfully, distutils makes this very easy:

from distutils.core import setup, Extension

module = Extension('module', libraries = ['opengl32'], sources=['module.c'])
## You'll need to add 'glu32' and 'gle32' if you used those in your module

setup(ext_modules = [module])

And that’s all you need to do. Your mileage may vary depending on your PyOpenGL version, the OpenGL library version, your OS, etc. (You may need to specify ‘library_dirs’ in your setup script. The relevant Documentation is here).

The engine only experienced a slight increase from this, but I will implement more speed ups soon (Display Lists, using arrays instead of lists for Polygon types, porting image module to C, etc). Expect code updates soon.

Written by bradzeis

June 28, 2010 at 9:45 pm

Posted in Flamingo, Python