EzPizza
EzPizza

Reputation: 1150

Pyglet: How do I only call draw when needed?

I have two processes communicating over queues. One is altering the application state, the other one is running the pyglet event-loop. (just a side note, this is likely not a multiprocessing issue). I only want to draw when the state has changed or possibly with interpolated steps in between.

I'm confused about two things here.

  1. I could write my own event-loop and check in there, if the state has changed. However even the minimal one from the docs is way less performant than the default app.run(). Why is that?

This is the loop from the documentation:

while True:
  pyglet.clock.tick()

  for window in pyglet.app.windows:
    window.switch_to()
    window.dispatch_events()
    window.dispatch_event('on_draw')
    window.flip()
  1. If I use app.run() (for said performance reasons) and I check for state-changes inside of on_draw(), then I get weird stuttering images between frames. It looks like the window is changing back and forth between multiple frames while I'm not drawing anything. Why?

My on_draw() would look something like this:

def on_draw(self):
  if self.check_if_state_changed():
    self.draw_the_new_state()

I know that I could prevent the stuttering like this:

def on_draw(self):
  if self.check_if_state_changed():
    self.draw_the_new_state()
  else:
    self.draw_the_old_state_again()

But I don't want to do that, because re-drawing the old state requires time and performance is crucial for this application.

Upvotes: 2

Views: 482

Answers (1)

EzPizza
EzPizza

Reputation: 1150

  1. I did not find out why the minimal loop is slower than app.run(), but it seems like the documentation is contradicting itself. The recommended way to change the event-loop is actually to subclass app.EventLoop and override the idle() method.
  2. The stuttering is due to the window being double buffered, which is probably for the better.

My solution to both problems is this, which will repeatedly call on_draw() as fast as possible:

class CustomLoop(app.EventLoop):
  def idle(self):    
    dt = self.clock.update_time()
    self.clock.call_scheduled_functions(dt)

    # Redraw all windows
    for window in app.windows:
        window.switch_to()
        window.dispatch_event('on_draw')
        window.flip()
        window._legacy_invalid = False

    # no timout (sleep-time between idle()-calls)
    return 0

app.event_loop = CustomLoop()
app.run() # locks the thread

I think their documentation should add a best-practice example for changing the event-loop.

Upvotes: 1

Related Questions