2

I am trying to visualise the output of a numerical fluid dynamics model using pygame. The model state is a (nx, ny) numpy array, which I am using surfarray.blit_array on a (nx, ny) surface that is then blitted to the screen. I have put a working example below (press ESC to stop it).

When screen_size is (nx, ny), on my computer the code runs at 60FPS. When I set the screen_size to (nx*5, ny), it drops to 14FPS.

I think I am doing the right thing to update the display, only updating the rects and not the whole screen, so why is it slowing down so precipitously when additional blank space is added to the screen? Using display.update seems to be no faster than using display.flip.

import pygame
import numpy as np
from matplotlib.cm import viridis
from matplotlib.colors import Normalize

pygame.init()
pygame.font.init()
font = pygame.font.SysFont("Arial", 18)

nx = 256
ny = 257

# CHANGE THIS TO SEE THE SLOWDOWN
screen_size = (nx, ny)

flags = pygame.DOUBLEBUF #|pygame.HWACCEL|pygame.FULLSCREEN
screen = pygame.display.set_mode(screen_size, flags)
screen.set_alpha(None)
clock = pygame.time.Clock()

def show_array(arr, surf=screen, cmap=viridis):
    normed = Normalize()(arr)
    cmapped = cmap(normed, bytes=True)[:,:, :3]
    pygame.surfarray.blit_array(surf, cmapped)

surf = pygame.Surface((nx, ny))
ocean = np.zeros((nx, ny))
x, y = np.indices(ocean.shape)
ocean[:] = np.cos(x/nx*2*np.pi)   # initial condition

for i in range(10000):
    active_rects = []
    clock.tick(100)
    # move the wave left (this is a stand-in for the real physics)
    ocean = np.roll(ocean, 1, axis=0)
    show_array(ocean, surf)
    active_rects.append(screen.blit(surf, (0, 0)))
    fps = font.render('FPS: %1.f'%clock.get_fps(), True, (255, 255, 255))
    active_rects.append(screen.blit(fps, (5, 5)))
    for e in pygame.event.get():
        if e.type == pygame.QUIT:
            exit()
        if e.type == pygame.KEYDOWN:
            if e.key == 27: exit()
    pygame.display.update(active_rects)
    #pygame.display.flip()
James P
  • 1,118
  • 1
  • 11
  • 13
  • 1
    I have no clue if Pygame is the culprint or there's a logical error in the way data is passed around. But I'll throw this (maybe useless) piece of information at you: https://stackoverflow.com/questions/9035712/numpy-array-is-shown-incorrect-with-pyglet Consider trying pyglet, which is usually a faster wrapper for GL applications. I get that it's re-learning a library, but it should suit your needs pretty well to render things quickly but not giving you tools like collision detection etc. – Torxed Jan 28 '19 at 21:29
  • If you need a base to get started in Pyglet, here's a minimal one with fast render times: https://github.com/Torxed/Scripts/blob/master/python/pyglet_base.py – Torxed Jan 28 '19 at 21:36
  • I cannot reproduce this issue using python 3.7 and pygame 1.4 on Ubuntu 18.04. Regardless of the size of nx and ny I always am at 100 FPS. – BradMcDanel Jan 29 '19 at 02:17
  • @BradMcDanel thanks for testing. I should have mentioned I was doing this on an iMac Pro, py3.7, pygame1.4. Could be mac backend dependent? – James P Jan 29 '19 at 09:37
  • @Torxed thanks for the tip, I'll take a look at pyglet. I'm new to pygame, not so far down the rabbit hole yet that I won't consider something completely different! – James P Jan 29 '19 at 09:39
  • @Torxed I followed your advice and took a look at pyglet. I ended up writing a wrapper that maps a numpy array to pyglet image, with a bit of hacking it seems to do the job. I've posted the full implementation here https://stackoverflow.com/a/54428173/420932 – James P Jan 29 '19 at 19:18
  • @JamesP - Nice piece of code, not bad for a first time pyglet user hehe. Nice abstractions as well. – Torxed Jan 29 '19 at 20:35

0 Answers0