#python #optimization #draw #pyglet #event-loop
#python #оптимизация #рисовать #pyglet #цикл событий
Вопрос:
У меня есть два процесса, обменивающиеся данными через очереди. Один изменяет состояние приложения, другой запускает цикл событий pyglet. (просто примечание, это, вероятно, не проблема многопроцессорности). Я хочу рисовать только тогда, когда состояние изменилось или, возможно, с интерполированными шагами между ними.
Здесь меня смущают две вещи.
- Я мог бы написать свой собственный цикл событий и проверить там, изменилось ли состояние. Однако даже минимальный вариант из документов намного менее производителен, чем стандартный 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()
- Если я использую 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:
- Я не выяснил, почему минимальный цикл работает медленнее, чем app.run() , но похоже, что документация противоречит сама себе. Рекомендуемый способ изменить цикл событий — фактически создать подкласс app.EventLoop и переопределить метод idle() .
- Заикание происходит из-за двойной буферизации окна, что, вероятно, к лучшему.
Мое решение обеих проблем заключается в следующем, которое будет многократно вызывать 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
Я думаю, что в их документацию следует добавить пример наилучшей практики для изменения цикла событий.