Search code examples
pythonpygameraspberry-piraspberry-pi4

Pygame slowdown on Raspberry Pi with multiple surfaces?


I'm trying to do a small visualizer project on a Raspberry Pi (was aiming for a 3B but went to a 4 to have the better processing capabilities), and I noticed an odd behavior that I can't account for.

Initially, I was just drawing to a main display surface (at 1920x1080 resolution) and the application was running faster than I'd expected a Pi to do.

However, I wanted to add a UI and be able to save the output free of the UI, so I broke my program up into multiple surfaces.

Before, I had code along the lines of:

# drawing multiple rectangles on the screen
for r in rects:
  pygame.draw.rect(display, color, r)

# blit the UI
txt = "This is my shortened UI"
label = font.render(txt, 1, (220, 220, 220))
display.blit(label, (20, 20))
pygame.display.update()

I updated it to using multiple surfaces so that I could directly save the mainSurf instead of the display surface. mainSurf is the same size as display, and uiSurf is set to 200x200.

def draw_rect_alpha(surf, col, rect):
    shape_surf = pygame.Surface(pygame.Rect(rect).size, pygame.SRCALPHA)
    pygame.draw.rect(shape_surf, col, shape_surf.get_rect())
    surf.blit(shape_surf, rect)

# .. in main loop

# drawing multiple rectangles on the screen
for r in rects:
  draw_rect_alpha(mainSurf, color, r)

# blit the UI
txt = "This is my shortened UI"
label = font.render(txt, 1, (220, 220, 220))
uiSurf.blit(label, (20, 20))

display.blit(mainSurf, (0, 0))
display.blit(uiSurf, (0, 0))
pygame.display.update()

Now, I may have missed a few lines of code as I'm transposing it manually, however the only real changes I made were to having multiple surfaces. I'm wondering if the issue how I'm rendering each rect, or having multiple surfaces in memory. The FPS dips significantly when using one approach versus the other.

Other notes - I'm using SDL1.2 at present, since I've been struggling to get SDL2 up and running on my Pi. I'm also using a pure terminal (no X windows) as well to minimize things running.


Solution

  • The problem is that creating a new "Pygame.Surface" object for each rectangle and each time it is drawn is very time consuming. I suggest creating the transparent Surface objects only once at initialization. Create a class for the transparent rectangles. Create the Surface object in the constructor and draw (blit) it in a methode of the class:

    class RectAlpha:
        def __init__(self, color, rect):
            self.rect = pygame.Rect(rect)
            self.surf = pygame.Surface(rect.size, pygame.SRCALPHA)
            pygame.draw.rect(self.surf, self.color, self.surf.get_rect())
        
        def draw(self, surf):
            surf.blit(self.surf, self.rect)
    

    The position of the rectangle can be changed by changing the position stored in the rect attribute. However, if you want to change the size, the object must be recreated.