0

I have the following code, which is a simplified from a project I'm working on. I thought the issue was the same as Why is the PyGame animation is flickering, but with the simplified code below, I've confirmed that I'm only calling pg.display.update() exactly once per pg.event.get() call. To be really explicit, I don't believe this is a duplicate of the linked question above because a) I have confirmed I only have one update() call already, which was the solution for the linked question; and b) it only happens on the CM4, not on when running on other computers.

That said, here's code that is able to reproduce the issue on a cm4:

import pygame as pg

class Game:
    def __init__(self):
        pg.init()

        self.__clock = pg.time.Clock()

        self.__window_size = (1024, 768)
        self.__project_size = (392, 291)

        self.screen = pg.display.set_mode(self.__window_size, pg.FULLSCREEN)

        self.blank_background = pg.transform.scale(pg.image.load("assets/default_background.png"),
                self.__window_size).convert()
        self.grid_image = pg.transform.scale(pg.image.load("assets/new_project_thumb.png"),
                self.__project_size).convert()

        self.project_selected = 0

        self.rect_pixels = 3

        self.grid_locations = self.calculate_grid_locations()

    def run(self) -> None:
        pg.mouse.set_visible(False)

        playing = True

        while playing:
            events = pg.event.get()

            button_event = None

            self.screen.blit(self.blank_background, (0, 0))

            for event in events:
                if event.type == pg.KEYDOWN:
                    button_event = event.key

            if button_event == pg.K_LEFT:
                self.project_selected = max((self.project_selected - 1), 0)

            if button_event == pg.K_RIGHT:
                self.project_selected = min((self.project_selected + 1), 3)

            if button_event == pg.K_ESCAPE:
                playing = False

            self.draw_menu()
            pg.display.update()
            self.__clock.tick(6)

    def draw_menu(self):
        for i in range(4):
            if i == self.project_selected:
                rect_loc = (self.grid_locations[i][0] - self.rect_pixels,
                            self.grid_locations[i][1] - self.rect_pixels)
                pg.draw.rect(self.screen,
                             (237, 28, 36),
                             pg.Rect(rect_loc,
                                     (self.__project_size[0] + self.rect_pixels * 2,
                                      self.__project_size[1] + self.rect_pixels * 2)))

            self.screen.blit(self.grid_image, self.grid_locations[i])
                                                        

    def calculate_grid_locations(self):
        w=int((self.__window_size[0] - 2 * self.__project_size[0]) / 3)
        h=int((self.__window_size[1] - 2 * self.__project_size[1]) / 3)

        return [(w, h),
                (w * 2 + self.__project_size[0], h),
                (w, h * 2 + self.__project_size[1]),
                (w * 2 + self.__project_size[0], h * 2 + self.__project_size[1])]

if __name__ == "__main__":
    game = Game()
    game.run()

The issue I'm having is that, specifically when I run it on a raspberry pi cm4 (and not on my linux laptop), I get intermittent flickering and/or lagging between frames - when I press an input, sometimes it will jitter back and forth between the image from the previous state and the current state very quickly before staying on the next frame.

One thing I have been able to figure out so far is that it might be framerate related - my laptop is easily able to hit 30 FPS, but the cm4 seems to only be able to do around 8 reliably (as reported by clock.get_fps()). Lowering the clock.tick() argument to below 8 does not fix the problem (as seen in the example code above) - there is still intermittent jittering even if the pi is able to maintain the framerate. It is also not specific to a given monitor - I have tried several.

This does not occur with the example games from the pygame library when they're run on the cm4, just my program.

Does anyone have any pointers for what to look into here? I'm not really sure where to examine this further. Hypothoses I've had include:

  1. Maybe the update() call happens asynchronously, and I need to find a way to block the game until it completes.

  2. Maybe I need to draw the menu images / selection rectangle as sprites. Looking at the code for the example games (which work great on the cm4), it looks like most of it is implemented as a sprite layer, and then just that is updated each cycle by passing the result of sprite.RenderUpdates().draw() as an argument to the update() call.

ZPears
  • 51
  • 7
  • The problem is not reproducible with information given in the question. – Rabbid76 Dec 08 '22 at 15:05
  • Yeah I recognize that - there are several thousand lines of code in the entire project, so it probably wouldn't be practical to reproduce the entire thing (especially since it seems hardware specific). I was mostly just hoping to get tips for other directions to investigate. – ZPears Dec 09 '22 at 02:28
  • For example, one hypothesis I had is that maybe display.update() happens asynchronously under the hood so maybe there is a race condition between multiple calls to it on the slower Pi. This seemed possible since I have confirmed I don't have multiple calls to update() per event.get() call, so there shouldnt be a way for frame n and frame n+1 to flicker back and forth, if everything were happening synchronously. – ZPears Dec 09 '22 at 02:31
  • See [What topics can I ask about here?](https://stackoverflow.com/help/on-topic): *"Questions seeking debugging help ("why isn't this code working?") must include the desired behavior, a specific problem or error **and the shortest code necessary to reproduce it in the question itself.**"* – Rabbid76 Dec 09 '22 at 07:25
  • OK, thanks for that feedback, I went through the code and created a greatly simplified version that is able to reproduce the issue on a cm4. – ZPears Dec 10 '22 at 03:49
  • One thought would be that maybe I need to draw the menu images / selection rectangle as sprites rather than as background? Looking at the code for the example games (which work great on the cm4), it looks like most of it is implemented as a sprite layer, and then just that is updated each cycle by passing the result of sprite.RenderUpdates().draw() as an argument to the update() call: https://github.com/xamox/pygame/blob/3e48d10fcf50d45a235bfd122f54d82b9711af44/examples/aliens.py#L308 – ZPears Dec 10 '22 at 04:31
  • Hello! I found a solution to this, and it wasn't the same as the linked question that you all marked as duplicate. Could you unlock this so I could post what I found? – ZPears Dec 10 '22 at 19:05

0 Answers0