Saturday, October 23, 2010

tricks for good 2d rendering performance with pygame

pygame's API for initializing a display has a couple attractive sounding flags. Or at least, they sound attractive once you notice that updating your 320x240 window at 30fps is consuming all available cycles on your brand new Intel i9 CPU. They're HWSURFACE and DOUBLEBUF. Hardware surfaces and double buffering is how you make graphics fast, right?



Well... no. You probably can't get a hardware surface anyway, and double buffering is unlikely to help until you figure out how to have a window larger than 320x240 anyway.



What you really need to do to get reasonable rendering performance is make sure that any images you blit to the display have the same color depth. You can do this with the Surface.convert method (you get back a Surface when you load an image from a file, eg a png or a gif). Check the depth of your display surface and then convert your image surfaces to that depth (and just keep the converted versions). Blitting them will get hundreds of times faster.



It's a pretty simple thing, but it's easy to get distracted by HWSURFACE and not notice the depth mismatch (like I did for three days).

8 comments:

  1. If you don't specify a format for convert(), it picks the one most suitable for blitting to the display surface. I.e. there's no need to manually check bit depths.

    ReplyDelete
  2. Eugh. Good to know. Though this begs the question why pygame.image.load doesn't just pick the most suitable depth. :) I suppose it has something to do with the fact that you can load an image before you initialize the display, but you can't convert an image until after you initialize the display. But then, why that arbitrary difference?

    ReplyDelete
  3. Probably because you might want to do something with an image other than blit it (also there's two ways to convert: .convert and .convert_alpha, picking either to be automatic would annoy people who want the other behavior), like say if I want to load a greyscale image to use as a heightmap: I'd like it to stay as a 8bpp image, not get converted to 32bpp because the user happens to be using a 32bpp display.

    ReplyDelete
  4. Is there any way to detect that some of your surfaces are in the wrong format, other than the performance hit?

    ReplyDelete
  5. I'm not aware of any way (but that doesn't mean a _whole_ lot).

    ReplyDelete
  6. I suppose. I probably wouldn't use the pygame image loading APIs for images I didn't intend to eventually blit, but maybe your explanation still makes sense in general.

    ReplyDelete
  7. Hey, why use pygame when there's pyglet?

    dotz

    ReplyDelete
  8. Partly because I started the project before pyglet was around (or at least before it was as usable as it is today).

    And partly because it's still not clear to me how to do networking with pyglet without re-implementing its three platform-specific event loops.

    ReplyDelete