Why is pyglet so slow compared to pygame?

萝らか妹 提交于 2019-12-21 02:21:14

问题


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

回答1:


The bottleneck is in pyglet sprite rotation. If you comment the 'self.rotation' line in Square update() method, your fps will almost double.




回答2:


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.



来源:https://stackoverflow.com/questions/19780029/why-is-pyglet-so-slow-compared-to-pygame

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!