Приложение Pyglet работает медленно - PullRequest
0 голосов
/ 29 июня 2018

У меня есть это приложение, которое создает альтернативную версию кликера cookie с именем pizza clicker. Это очень просто, но работает очень медленно, и я не могу понять, почему.

import pyglet
window = pyglet.window.Window(fullscreen=True, caption="Click For Pizzas", resizable=True)
win_width = window.width
win_height = window.height
window.set_fullscreen(False)
window.set_size(win_width, win_height)
image_height = round(int(win_width/5)/1.4, 1)
class Main(object):
    def __init__(self):
        self.label = pyglet.text.Label('Pizzas: 0', font_size=100, color=(0, 0, 0, 255),
                                   x=win_width//2, y=win_height - 100,
                                   anchor_x='left', anchor_y='top')
        self.points = 0
        self.number = 1
    def background(self):
        background_img = pyglet.resource.image('pizza_clicker.png')
        background_img.width = (win_width/5)*4
        background_img.height = win_height
        background_img.blit(int(win_width/5), 0, 0.5)
    def drawSidebar(self):
        width = int(win_width/5)
        height = int(win_height)
        sidebar_pattern = pyglet.image.SolidColorImagePattern(color=(100, 100, 100, 100))
        sidebar = sidebar_pattern.create_image(width, height)
        sidebar.blit(0, 0)
        pizza = []
        images = ('pizza_1.png', 'pizza_5.png', 'pizza_5.png', 'pizza_5.png')
        for i in range (0, len(images)):
          divideby = 1.4 / (i + 1)
          pizza.append(pyglet.resource.image(images[i]))
          pizza[i].width = width
          pizza[i].height = round(width/1.4, 1)
          pizza[i].blit(0, window.height - (round(width/divideby, 1)))
    def getNumber(self, y):
        if y > window.height - int(image_height):
            self.number = 1
        elif y > window.height - (int(image_height)*2):
            self.number = 5
        elif y > window.height - (int(image_height)*3):
            self.number = 10
        elif y > window.height - (int(image_height)*4):
            self.number = 20
    def addPoint(self):
       self.points += self.number
       self.label.text = 'Pizzas: %s' %self.points


@window.event
def on_mouse_press(x, y, buttons, modifiers):
    if buttons & pyglet.window.mouse.LEFT and x > win_width/5:
        main.addPoint()
    elif buttons & pyglet.window.mouse.LEFT and x < win_width/5:
        main.getNumber(y)

@window.event
def on_draw():
    window.clear()
    main.background()
    main.label.draw()
    main.drawSidebar()

main = Main()

pyglet.app.run()

Таким образом, проблема в том, что когда я нажимаю на правую часть окна, оно должно добавить точку (или много) мгновенно, но отстает на несколько секунд. Кроме того, чтобы никто не запутался, код работает, но просто медленно. Что я должен сделать, чтобы решить это?

1 Ответ

0 голосов
/ 01 июля 2018

На каждой draw() итерации вы выполняете:

background_img = pyglet.resource.image('pizza_clicker.png')

Это означает, что вы загружаете одну и ту же картинку с жесткого диска для каждой последовательности рендеринга. Вы также делаете цикл для различных изображений пиццы, где вы также получаете их с жесткого диска:

for i in range (0, len(images)):
    divideby = 1.4 / (i + 1)
    pizza.append(pyglet.resource.image(images[i]))

Я настоятельно рекомендую вам прочитать о том, как загружаются ресурсы, и использовать анализатор cProfiler.

Хороший пример того, как вы могли бы профилировать свой код, это здесь . Поскольку это внешний источник, я также включу две ссылки SO, которые примерно одинаково хороши (но не настолько сильны или самоочевидны):

Вот версия tl-dr:

python -m cProfile -o profile_data.pyprof your_script.py
pyprof2calltree -i profile_data.pyprof -k

Это должно отобразить так называемое «дерево вызовов» всех выполнений, которые выполнял ваш код, сколько времени они заняли и сколько памяти они использовали. Весь путь от начала до конца вашего приложения.

Однако я настоятельно рекомендую вам сделать 1 последовательность рендеринга и добавить exit(1) после первого рендера. Просто так у вас в профиле 1 пробег, а не 60 в секунду.

Ключевые слова для поиска, чтобы понять, почему ваш код работает медленно: Python, профилирование, kcachegrind, cprofile, cprofiling, callstack.

Оповещение о спойлере

Чтобы решить большинство ваших проблем, переместите все интенсивные операции ввода-вывода (загрузка изображений, создание фигур и т. Д.) В вызов __init__ вашего основного класса.

Конечный продукт будет выглядеть примерно так:

class Main(object):
    def __init__(self):
        self.label = pyglet.text.Label('Pizzas: 0', font_size=100, color=(0, 0, 0, 255),
                                   x=win_width//2, y=win_height - 100,
                                   anchor_x='left', anchor_y='top')
        self.points = 0
        self.number = 1

        self.background_img = pyglet.resource.image('pizza_clicker.png')
        self.background_img.width = (win_width/5)*4
        self.background_img.height = win_height

        sidebar_pattern = pyglet.image.SolidColorImagePattern(color=(100, 100, 100, 100))
        self.sidebar = sidebar_pattern.create_image(width, height)

        self.pizzas = []
        width = int(win_width/5)
        height = int(win_height)
        self.pizza_images = ('pizza_1.png', 'pizza_5.png', 'pizza_5.png', 'pizza_5.png')
        for i in range (0, len(pizza_images)):
            resource = pyglet.resource.image(pizza_images[i])
            resource.width = width
            resource.height = round(width/1.4, 1) # Not sure why you're using width here.. meh.. keeping it -yolo-
            self.pizzas.append(resource)

    def background(self):
        self.background_img.blit(int(win_width/5), 0, 0.5)

    def drawSidebar(self):
        width = int(win_width/5)
        height = int(win_height) # You're using win_height here, but window.height later. It's strange.
        self.sidebar.blit(0, 0)
        for i in range (0, len(self.pizza_images)):
            divideby = 1.4 / (i + 1)
            self.pizzas[i].blit(0, window.height - (round(width/divideby, 1)))

    def getNumber(self, y):
        if y > window.height - int(image_height):
            self.number = 1
        elif y > window.height - (int(image_height)*2):
            self.number = 5
        elif y > window.height - (int(image_height)*3):
            self.number = 10
        elif y > window.height - (int(image_height)*4):
            self.number = 20

    def addPoint(self):
       self.points += self.number
       self.label.text = 'Pizzas: %s' %self.points

Но зачем останавливаться на достигнутом, здесь широко используется blit. Блит хорош для одного или двух объектов. Но быстро становится трудно отследить, что и где вы "все делаете". Вы также выполняете большое количество делений, сложений и других вычислений в циклах и прочем.

Помните, что циклы - это дьявол, когда дело доходит до рендеринга.
Если у вас есть цикл где-то, вы почти наверняка начнете искать проблемы с производительностью (любой, кто посмотрит на этот комментарий и скажет "пф, он понятия не имеет, что говорит" ... Да, я знаю, но это хорошие новички кончик) .

Я настоятельно рекомендую вместо этого поместить ваши изображения в pyglet.sprite.Sprite () . Они отслеживают позиции, рендеринг и, самое главное, они поддерживают пакетный рендеринг . Это ваш святой Грааль Родины! Если что-то и поможет вам повысить производительность в pyglet ... ну ... 3D-рендеринг в целом, это пакетный рендеринг.

Видите ли, графическая карта была разработана с одной целью ... Взять ОГРОМНОЕ математическое уравнение и просто проглотить его целиком. Это особенно хорошо - брать большую жирную стопку информации и просто снимать ее на экран. Это не так хорошо для нескольких команд. Это означает, что если вы отправляете много меньших пакетов назад и в-четвертых на видеокарту, она не будет изнашиваться почти оптимально из-за накладных расходов и других вещей.

Итак, помещая ваши изображения в спрайты, и помещая эти спрайты в пакеты, и не используя их для циклов и загрузки ресурсов при рендеринге ..

Вот как будет выглядеть ваш код:

class Main(object):
    def __init__(self):
        self.label = pyglet.text.Label('Pizzas: 0', font_size=100, color=(0, 0, 0, 255),
                                   x=win_width//2, y=win_height - 100,
                                   anchor_x='left', anchor_y='top')
        self.points = 0
        self.number = 1

        self.background_layer = pyglet.graphics.OrderedGroup(0)
        self.foreground_layer = pyglet.graphics.OrderedGroup(1)
        self.batch = pyglet.graphics.Batch()

        self.background_img = pyglet.sprite.Sprite(pyglet.resource.image('pizza_clicker.png'), batch=self.batch, group=self.background_layer)
        self.background_img.width = (win_width/5)*4
        self.background_img.height = win_height
        self.background.x = int(win_width/5)
        self.background.y = 0

        sidebar_pattern = pyglet.image.SolidColorImagePattern(color=(100, 100, 100, 100))
        self.sidebar = pyglet.sprite.Sprite(sidebar_pattern.create_image(width, height), batch=self.batch, group=self.background_layer)
        self.sidebar.x = 0
        self.sidebar.y = 0

        self.pizzas = []
        width = int(win_width/5)
        height = int(win_height)
        self.pizza_images = ('pizza_1.png', 'pizza_5.png', 'pizza_5.png', 'pizza_5.png')
        for i in range (0, len(pizza_images)):
            divideby = 1.4 / (i + 1)

            resource = pyglet.sprite.Sprite(pyglet.resource.image(pizza_images[i]), batch=self.batch, group=self.foreground_layer)
            resource.width = width
            resource.height = round(width/1.4, 1) # Not sure why you're using width here.. meh.. keeping it -yolo-
            resource.x = 0
            resource.y = window.height - (round(width/divideby, 1))

            self.pizzas.append(resource)

    def draw(self):
        # This is instead of doing:
        # - self.background.draw()
        # - self.sidebar.draw()
        # - self.pizzas[i].draw()
        self.batch.draw()
        self.label.draw() # You could put this in a batch as well :)

    def getNumber(self, y):
        if y > window.height - int(image_height):
            self.number = 1
        elif y > window.height - (int(image_height)*2):
            self.number = 5
        elif y > window.height - (int(image_height)*3):
            self.number = 10
        elif y > window.height - (int(image_height)*4):
            self.number = 20

    def addPoint(self):
        self.points += self.number
        self.label.text = 'Pizzas: %s' %self.points

@window.event
def on_draw():
    window.clear()
    main.draw()

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

...