Increase blitting speed?

I'm working on a 2d sidescroller in PyGame. For each map we use one texture (this is the actual size of the texture):

Then we load the image with this code:

sprite = pygame.image.load("Maps/MapTesting.png")
sprite.convert()
sprite = pygame.transform.scale(sprite,
(sprite.get_width()*6, sprite.get_height()*6))


As you can see, the texture gets blown up 6 times to create the actual map texture. On average this texture is about 4500x800. This texture has to be blitted to the screen every frame, because the whole screen is dirty (thanks to the side scrolling). We do that using this code:

screen.blit(sprite, (0, 0),
(cameraposx, cameraposy, windowheight, windowwidth))


And it works. The problem is is that it's quite slow: I get a meager 40 FPS on a low-decent PC, and that's without any of the actual AI/objects going on, while we're aiming for 60 FPS. How can we speed this up?

Note that the above code is sanitized and taken out of context. The full code can be found here: https://github.com/nightcracker/PyGG2

And last but not least, while the above image might look like 8-bit, there are elements in the game which require more bit-depth.

• Split to a sane size per texture, say 1024 wide per block, and only blit the two that are relevant at a time. Sep 29 '11 at 12:59
• @JariKomppa: Tried it, has no effect on the blitting (pygame is smart enough to blit only the rect I tell it to), only memory usage.
– orlp
Sep 29 '11 at 13:03
• You could... not use pygame (trollface.jpg) .... jkjk, in this case I'm guessing that's not where your slowdown is occurring, have you tested it extensively? Sep 29 '11 at 15:43
• @ultifinitus: Yes, I profiled it with cProfile.
– orlp
Sep 29 '11 at 15:54
• What's the speed difference if you display it at it's actual resolution? What if you precompute the larger image? Sep 29 '11 at 15:58

Let me list some general compared optimizations related to Blitting pixels to a surface(from my experience).

1)Usually palette images(indexed images) when blitted, will under go one extra level of redirection (to get the color).So they will be slow when blitting when compared to true color images.

2)True color pixel data (assume With out Alpha - say24 bit data) can be blitted very fast as we can do a memcpy for each scanline of the image(if we are copying a part of the image) on to the device frame buffer.If the data to be blitted is a full image, then we can directly memcpy the whole data which is much faster!

3)Blitting Alpha pixel data will be the costliest as it will include calculating the each component resultant and we need to pack it again to RGB data. In simple terms more operations for each pixel for getting final color!

Finalrgb = Alpha*(Srgb) + (1-Alpha)*Drgb (this is for normal blend equation)
where Srgb is source-rgb (we need to apply for each of the component for final color)
Drgb is the color that will be there in the destination buffer.


I haven't worked on pyGame before.But , A quick look on souce code of it, lead me to assume that it is using 'sdl' Blit functions under the hood. Usually Sdl blit will be very faster and optimized so, just make a not of the above points and profile once again! Good luck!

*Update:*Setting color key is like adding one extra check when blitting each pixel to the surface.Some thing like this -

       for(eachPixelColor in allPixels)
{
if(eachPixelColor is NOT colorKeyColor)
{
copy color to the frame buffer!
}

}


So, here if you see, we are limited not to use memcpy as we need to check each pixel's color validity!

• Ok, as it turns out a call to set_colorkey slipped in on the map texture, giving it an (expensive) alpha channel. A convert fixed that, and now I'm running 150 FPS stable on this crap PC. Thanks!
– orlp
Sep 29 '11 at 20:09
• hey will edit related to color key even forgot to add some info abt it :D Sep 30 '11 at 2:41

sprite.convert() doesn't do what you think it does.

sprite = sprite.convert() is what you need.

• Woops, I do have sprite = sprite.convert() in the real code though :)
– orlp
Sep 29 '11 at 19:58
• Aha, good. :) In that case, I don't have much to offer you except to consider using pyglet instead of pygame, or use pyOpenGL directly if you're comfortable with OpenGL. Sep 29 '11 at 20:04
• @Kylotan:good catch mate! i haven't checked it :) Sep 30 '11 at 2:48
• or, even better: sprite = pygame.image.load("Maps/MapTesting.png").convert() Aug 14 '14 at 0:26