Here is the code. 5000 bouncing spinning red squares. (16x16 png) On the pygame version I get 30 fps but 10 fps with pyglet. Isnt OpenGl supposed to be faster for this kind of thing?
pygame version:
import pygame, sys, random
from pygame.locals import *
import cProfile
# Set FPS
FPS = 60.0
clock = pygame.time.Clock()
# Set window
WINDOWWIDTH= 800
WINDOWHEIGHT = 600
pygame.init()
screen = pygame.display.set_mode((WINDOWWIDTH,WINDOWHEIGHT))
screen.fill((0,0,0))
background = screen.copy().convert()
image = pygame.image.load("square.png").convert()
class Square(object):
def __init__(self,x,y):
self.x = x
self.y = y
self.v_x = random.randint(1,100)
self.v_y = random.randint(1,100)
self.v_r = random.randint(-100,100)
self.rotation = 0
def __rep__(self):
return "Square %d,%d"%(self.x,self.y)
def update(self,dt):
if self.x > WINDOWWIDTH:
self.v_x *= -1
elif self.x < 0:
self.v_x *= -1
if self.y > WINDOWHEIGHT:
self.v_y *= -1
elif self.y < 0:
self.v_y *= -1
self.x += self.v_x * dt
self.y += self.v_y * dt
self.rotation += self.v_r * dt
def draw(self):
screen.blit(pygame.transform.rotate(image,self.rotation),(self.x,self.y))
sqrs = []
for _ in range(5000):
sqrs.append( Square(random.randint(0,WINDOWWIDTH-1),random.randint(0,WINDOWHEIGHT-1)) )
def main_loop():
tick = 0.0
elapsed = 0.0
while elapsed < 10.0:
dt = tick/1000.0
# Events
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
# Logic
for s in sqrs:
s.update(dt)
# Drawing
screen.blit(background,(0,0))
for s in sqrs:
s.draw()
pygame.display.update()
pygame.display.set_caption('test program FPS: %s'%(clock.get_fps() ) )
tick = clock.tick(FPS)
elapsed += tick/1000.0
pygame.quit()
cProfile.run("main_loop()")
i = input("...")
pyglet version:
import cProfile
import pyglet, random
# Disable error checking for increased performance
pyglet.options['debug_gl'] = False
from pyglet import clock
clock.set_fps_limit(60)
WINDOWWIDTH = 800
WINDOWHEIGHT = 600
FPS = 60.0
batch = pyglet.graphics.Batch()
window = pyglet.window.Window(WINDOWWIDTH,WINDOWHEIGHT)
fps_display = pyglet.clock.ClockDisplay()
image = pyglet.resource.image("square.png")
class Square(pyglet.sprite.Sprite):
def __init__(self,x,y):
pyglet.sprite.Sprite.__init__(self,img = image,batch=batch)
self.x = x
self.y = y
self.v_x = random.randint(1,100)
self.v_y = random.randint(1,100)
self.v_r = random.randint(-100,100)
def update(self,dt):
if self.x > WINDOWWIDTH:
self.v_x *= -1
elif self.x < 0:
self.v_x *= -1
if self.y > WINDOWHEIGHT:
self.v_y *= -1
elif self.y < 0:
self.v_y *= -1
self.x += self.v_x * dt
self.y += self.v_y * dt
self.rotation += self.v_r * dt
sqrs = []
for _ in range(5000):
sqrs.append( Square(random.randint(0,WINDOWWIDTH-1),random.randint(0,WINDOWHEIGHT-1)) )
elapsed = 0.0
def update(dt):
global elapsed
elapsed += dt
if elapsed >= 10.0:
clock.unschedule(update)
window.close()
else:
for s in sqrs:
s.update(dt)
@window.event
def on_draw():
window.clear()
batch.draw()
fps_display.draw()
clock.schedule_interval(update, 1.0/FPS)
if __name__ == '__main__':
cProfile.run("pyglet.app.run()")
c = input("...")
cProfile result for pygame:
5341607 function calls in 9.429 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 9.429 9.429 <string>:1(<module>)
1335000 2.259 0.000 2.259 0.000 pygame-test.py:32(update)
1335000 1.323 0.000 5.969 0.000 pygame-test.py:46(draw)
1 0.772 0.772 9.429 9.429 pygame-test.py:55(main_loop)
1 0.000 0.000 9.429 9.429 {built-in method exec}
267 0.020 0.000 0.020 0.000 {built-in method get}
1 0.237 0.237 0.237 0.237 {built-in method quit}
1335000 3.479 0.000 3.479 0.000 {built-in method rotate}
267 0.013 0.000 0.013 0.000 {built-in method set_caption}
267 0.067 0.000 0.067 0.000 {built-in method update}
1335267 1.257 0.000 1.257 0.000 {method 'blit' of 'pygame.Surface' objects}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
267 0.000 0.000 0.000 0.000 {method 'get_fps' of 'Clock' objects}
267 0.001 0.000 0.001 0.000 {method 'tick' of 'Clock' objects}
Pyglet cProfile output: - Very long, this is partial output, full version here.
9982775 function calls (9982587 primitive calls) in 10.066 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
123 0.000 0.000 0.000 0.000 <frozen importlib._bootstrap>:1596(_handle_fromlist)
1 0.000 0.000 10.067 10.067 <string>:1(<module>)
11 0.000 0.000 0.000 0.000 __init__.py:1055(_ensure_string_data)
11 0.000 0.000 0.000 0.000 __init__.py:1061(_get_gl_format_and_type)
58 0.000 0.000 0.000 0.000 __init__.py:1140(clear)
75 0.000 0.000 0.012 0.000 __init__.py:1148(dispatch_event)
1 0.000 0.000 10.067 10.067 __init__.py:115(run)
...
1 0.000 0.000 0.000 0.000 lib.py:124(decorate_function)
1108 0.005 0.000 0.005 0.000 lib_wgl.py:80(__call__)
285000 1.409 0.000 9.872 0.000 pyglet-test.py:29(update)
58 0.105 0.002 9.982 0.172 pyglet-test.py:49(update)
...
855000 5.436 0.000 7.551 0.000 sprite.py:378(_update_position)
285000 0.172 0.000 2.718 0.000 sprite.py:441(_set_x)
851800 0.177 0.000 0.177 0.000 sprite.py:445(<lambda>)
285000 0.174 0.000 2.670 0.000 sprite.py:451(_set_y)
851115 0.155 0.000 0.155 0.000 sprite.py:455(<lambda>)
285000 0.182 0.000 2.692 0.000 sprite.py:461(_set_rotation)
285000 0.051 0.000 0.051 0.000 sprite.py:465(<lambda>)
...
4299 0.007 0.000 0.025 0.000 vertexattribute.py:308(get_region)
1 0.000 0.000 0.000 0.000 vertexattribute.py:380(__init__)
116 0.000 0.000 0.000 0.000 vertexattribute.py:384(enable)
116 0.000 0.000 0.000 0.000 vertexattribute.py:387(set_pointer)
1 0.000 0.000 0.000 0.000 vertexattribute.py:461(__init__)
116 0.000 0.000 0.000 0.000 vertexattribute.py:466(enable)
116 0.000 0.000 0.000 0.000 vertexattribute.py:469(set_pointer)
1 0.000 0.000 0.000 0.000 vertexattribute.py:501(__init__)
116 0.000 0.000 0.000 0.000 vertexattribute.py:508(enable)
116 0.000 0.000 0.000 0.000 vertexattribute.py:511(set_pointer)
3 0.000 0.000 0.000 0.000 vertexbuffer.py:293(__init__)
348 0.000 0.000 0.001 0.000 vertexbuffer.py:311(bind)
348 0.000 0.000 0.001 0.000 vertexbuffer.py:314(unbind)
3 0.000 0.000 0.000 0.000 vertexbuffer.py:381(__init__)
348 0.001 0.000 0.004 0.000 vertexbuffer.py:388(bind)
4299 0.006 0.000 0.016 0.000 vertexbuffer.py:420(get_region)
3 0.000 0.000 0.000 0.000 vertexbuffer.py:424(resize)
4299 0.002 0.000 0.002 0.000 vertexbuffer.py:460(__init__)
855232 0.735 0.000 1.053 0.000 vertexbuffer.py:466(invalidate)
...
855058 0.687 0.000 1.762 0.000 vertexdomain.py:581(_get_vertices)
...
4300 0.002 0.000 0.002 0.000 {built-in method POINTER}
...
841451 0.162 0.000 0.162 0.000 {built-in method cos}
...
2417/2415 0.000 0.000 0.000 0.000 {built-in method len}
855489 0.142 0.000 0.142 0.000 {built-in method max}
855469 0.176 0.000 0.176 0.000 {built-in method min}
465/407 0.000 0.000 0.000 0.000 {built-in method next}
...
841451 0.072 0.000 0.072 0.000 {built-in method radians}
62 0.000 0.000 0.000 0.000 {built-in method setattr}
841451 0.120 0.000 0.120 0.000 {built-in method sin}
...
The bottleneck is in pyglet sprite rotation. If you comment the 'self.rotation' line in Square update() method, your fps will almost double.
Pyglet images (and sprites) can be rotated around an arbitrary anchor. If you look at pyglet/sprite.py
, you will see that this is done using the Python math module, which is why it's rather slow. It would seem there is room for optimiziation here, by rotating sprites using OpenGL glRotate or even a vertex shader.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With