9

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

map 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.

orlp
  • 364
  • 3
  • 11
  • Split to a sane size per texture, say 1024 wide per block, and only blit the two that are relevant at a time. – Jari Komppa 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? – ultifinitus 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? – ultifinitus Sep 29 '11 at 15:58
  • did you try it with power of two textures? – Jari Komppa Sep 29 '11 at 16:17
  • Jari Komppa: No, why? – orlp Sep 29 '11 at 17:00
  • Graphics cards like power of two textures. – Jari Komppa Sep 29 '11 at 17:28
  • Also, instead of growing the texture yourself, why not let the graphics card do it while blitting? – Jari Komppa Sep 29 '11 at 17:28
  • Jari Komppa: I grow it once, instead of every blit. – orlp Sep 29 '11 at 17:34
  • @nightcracker that just increases memory bandwidth; copying data doesn't really cost here, bandwidth does. – Jari Komppa Sep 29 '11 at 18:50
  • I don't think Pygame -can- scale during blits, but if it can, expecting it to be efficient is a bad idea. It's based on old 2D APIs, many of which are no longer optimal. It's likely the sprite data is stored in main memory no matter what you do, although passing HWSURFACE to pygame.display.set_mode() may improve your chances on some systems. – Kylotan Sep 29 '11 at 19:12
  • @Jari: NPOT textures are totally not related to anything here, Pygame has nothing we'd call hardware acceleration. –  Sep 29 '11 at 19:26
  • @JoeWreschnig Oh, that explains a bunch. Scaling up at blit time still may help with the bandwidth. Googling pygame opengl gave a bunch of tutorials and such, so if you do want faster blits, using opengl is probably the best way =) – Jari Komppa Sep 30 '11 at 05:04

2 Answers2

7

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!

Ayyappa
  • 870
  • 5
  • 13
  • 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 – Ayyappa Sep 30 '11 at 02:41
5

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

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

Kylotan
  • 24,329
  • 3
  • 51
  • 94