Pyglet: как мне вызывать draw только при необходимости?

#python #optimization #draw #pyglet #event-loop

#python #оптимизация #рисовать #pyglet #цикл событий

Вопрос:

У меня есть два процесса, обменивающиеся данными через очереди. Один изменяет состояние приложения, другой запускает цикл событий pyglet. (просто примечание, это, вероятно, не проблема многопроцессорности). Я хочу рисовать только тогда, когда состояние изменилось или, возможно, с интерполированными шагами между ними.

Здесь меня смущают две вещи.

  1. Я мог бы написать свой собственный цикл событий и проверить там, изменилось ли состояние. Однако даже минимальный вариант из документов намного менее производителен, чем стандартный app.run() . Почему это так?

Это цикл из документации:

 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. Если я использую app.run() (по указанным причинам производительности) и проверяю изменения состояния внутри on_draw(), то я получаю странные изображения с заиканием между кадрами. Похоже, что окно меняется взад и вперед между несколькими кадрами, пока я ничего не рисую. Почему?

Мой on_draw() будет выглядеть примерно так:

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

Я знаю, что мог бы предотвратить подобное заикание:

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

Но я не хочу этого делать, потому что повторное рисование старого состояния требует времени, а производительность имеет решающее значение для этого приложения.

Ответ №1:

  1. Я не выяснил, почему минимальный цикл работает медленнее, чем app.run() , но похоже, что документация противоречит сама себе. Рекомендуемый способ изменить цикл событий — фактически создать подкласс app.EventLoop и переопределить метод idle() .
  2. Заикание происходит из-за двойной буферизации окна, что, вероятно, к лучшему.

Мое решение обеих проблем заключается в следующем, которое будет многократно вызывать on_draw() как можно быстрее:

 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
 

Я думаю, что в их документацию следует добавить пример наилучшей практики для изменения цикла событий.