0

I am thinking about porting a project over from PySFML to Pyglet. SFML is a bit of a wonky library, and even though it runs decently enough, I thought I would take a look at Pyglet. Unfortunately, Pyglet displays erratic FPS changes when doing simple things (between 20 and 800 FPS), and barely manages to draw frames when I try to do anything fancier.

import pyglet

window = pyglet.window.Window()
fps_display = pyglet.clock.ClockDisplay()
labelList = []
for i in range(100):
    label = pyglet.text.Label('Hello, world',
        font_name='Times New Roman',
        font_size=36,
        x=window.width//2, y=window.height//2+i,
        anchor_x='center', anchor_y='center')
    labelList.append(label)


def main():
    pyglet.clock.schedule_interval(update, 1/30.0)
    pyglet.app.run()

def update(dt):
    for la in labelList:
        la.x += 1

@window.event
def on_draw():
    window.clear()
    for la in labelList:
        la.draw()
    fps_display.draw()


if __name__ == "__main__":
    main()

The example is goofy, but I'm just taking 100 labels and moving them across the screen. This runs at about 7 fps on my machine. Using SFML, drawing 500 sprites and handling input gives me 200ish FPS.

3 Answers3

2

Consider using a Batch to draw all your labels at once:

lbls = pyglet.graphics.Batch()
for i in range(100):
    label = pyglet.text.Label('Hello, world',
        font_name='Times New Roman', [...], batch=lbls)
labelList.append(label)
[...]
lbls.draw()

While your example runs with a few less fps than your targeted 30 on my machine, this enhancement enables it to run at ~60 fps. Try to increase the frequency of update() calls specified via schedule_interval(update,...) to find out what framerate you can achieve.

Another thing is that every assignment to a label's x member causes its _update() method to be called, which seems to re-build the visual representation of the label's text content entirely from scratch every time. Some performance gain is thus imaginable via inheriting the Label class and overwriting its _set_x() method in order to suppress those _update() calls (See this question regarding corresponding behaviour of the Sprite class).

Community
  • 1
  • 1
J. Katzwinkel
  • 1,923
  • 16
  • 22
  • Thanks for the suggestion, J. I still believe there are deeper issues here. Even a very basic program which moves a single sprite across the screen doesn't manage to hit 60 FPS. Pyglet is obviously unusable for me in this state. Hmm... – user3076291 Feb 05 '14 at 19:31
2

In addition to using batches I got noticeable performance gains (on certain machines) by disabling debug_gl right after importing pyglet:

import pyglet
pyglet.options['debug_gl'] = False
Sebastian
  • 5,721
  • 3
  • 43
  • 69
0

Restructuring the code as a class helped increase the performance on my computer. Even without the batch graphics call, it can draw 100 labels. I am not sure why this improves the performance (it just removes the decorator).

import pyglet

class GameWindow(pyglet.window.Window):
    def __init__(self, *args, **kwargs):
        super(GameWindow, self).__init__(*args, **kwargs)
        pyglet.clock.schedule_interval(self.update, 1.0/60.0)
        self.labelList = []
        self.fps_display = pyglet.clock.ClockDisplay()
        self.labelBatch = pyglet.graphics.Batch()
        for i in range(1000):
            label = pyglet.text.Label('Hello, world',
            font_name='Times New Roman',
            font_size=36,
            x=self.width//2, y=self.height//2+i,
            anchor_x='center', anchor_y='center',
            batch = self.labelBatch)
            self.labelList.append(label)

    def update(self, dt):
        for la in self.labelList:
            la.x += 1

    def on_draw(self):
        self.clear()
        self.labelBatch.draw()
        self.fps_display.draw()

if __name__ == "__main__":
    game = GameWindow(width=800, height=600)
    pyglet.app.run()
CodeSurgeon
  • 2,435
  • 2
  • 15
  • 36