Как улучшить производительность дозирования

9

Я разрабатываю 2D-игру на основе спрайтов для мобильных платформ и использую OpenGL (ну, собственно, Irrlicht) для рендеринга графики. Сначала я реализовал рендеринг спрайтов простым способом: каждый игровой объект визуализируется как квад с собственным вызовом отрисовки в графическом процессоре. Это означает, что если у меня было 200 игровых объектов, я делал 200 вызовов отрисовки на кадр. Конечно, это был плохой выбор, и моя игра была полностью привязана к процессору, потому что при каждом вызове отрисовки графического процессора было связано небольшое количество ресурсов процессора. GPU большую часть времени простаивал.

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

Почему? Ну, у меня есть 200 (или больше) игровых объектов, и они обновляются 60 раз в секунду. Каждый кадр мне приходится пересчитывать новую позицию (перемещение и вращение) для вершин в CPU (GPU на мобильных платформах не поддерживает создание экземпляров, поэтому я не могу сделать это там), и выполняю этот расчет 48000 в секунду (200 * 60 * 4, так как каждый спрайт имеет 4 вершины) просто кажется слишком медленным.

Что я могу сделать, чтобы улучшить производительность? Все игровые объекты движутся / вращаются (почти) в каждом кадре, поэтому мне действительно нужно пересчитать позиции вершин. Единственная оптимизация, о которой я мог подумать, - это таблица поиска поворотов, чтобы мне не пришлось их вычислять. Помогут ли точечные спрайты? Какие-нибудь неприятные хаки? Что-нибудь еще?

Спасибо.

user4241
источник

Ответы:

5

Вы использовали мой порт irrlicht для Android? Для 2d спрайтов на Android и iphone я использую те же приемы, что и вы: пакетирование. Я пробую много решений в OpenGL ES 1.x и 2.x:

  • Сортируйте по z (параллакс) и по текстуре, делайте преобразования на CPU и вызывайте glDrawArrays или glDrawElements (самый быстрый способ). Используйте одну большую текстуру, если можете.
  • Тот же трюк с VBO, не быстрее, потому что для каждого кадра вы обновляете всю информацию. Это может быть полезно для статических спрайтов.
  • используйте OpenGL ES 2.x и используйте вершинный шейдер для вычисления позиций (медленнее)
  • использовать PointSprites (нет решения, если это не квадрат и слишком много прозрачных пикселей убивают скорость заполнения)
  • использовать расширение gldrawtexoes ...
  • использовать DrawCall для каждого спрайта (самый медленный метод)

Итак, как и вы, все преобразования выполняются процессором для OGLES 1.x или OGLES 2.x. Если у вас есть неоновые инструкции, вы можете использовать их для ускорения вычислений.

Ps: на устройствах iphone или android, я не ограничен ЦП, но ограничен уровень заполнения. Поэтому очень важно ограничить овердрейт.

Эллис
источник
Отлично, это то, что я искал. Я не знал о вашем порте Irrlicht, но у меня уже есть версия Irrlicht, работающая на iOS. Вы говорите, что не ограничены процессором - сколько спрайтов вы рисуете? А какова ваша частота кадров, скажем, для 100 спрайтов на iPhone? Если у меня 200 объектов, я выполняю 48000 вычислений в секунду. Ваша точка зрения о fillrate хорошая.
user4241
Статические спрайты (фон) есть в VBO. Я использую один VBO на параллакс. В противном случае, у меня есть 100-200 спрайтов на Moblox. На всех iphone, включая 3G, у меня более 30 кадров в секунду (насколько я помню). Но большие спрайты очень дороги (проблема с заполнением) ...
Эллис
Я работаю над движком частиц, который я могу использовать до 20 000 частиц со всеми вычислениями позиций, выполненными на CPU, и у меня есть 10 кадров в секунду с экстремальными настройками (на 3GS и iPhone4). Таким образом, 1000 спрайтов должны быть возможны на 3GS или iPhone4 с хорошей частотой кадров.
Эллис
Спасибо, очень помогли! Как вы реализуете свой движок частиц? Я полагаю, вы играете с шейдерами?
user4241
Я использую шейдеры, потому что мне нужно gl_PointSize для настройки каждого размера частиц. Я больше не работаю с OGLES 1.x, потому что старые телефоны не моя цель. Сначала весь мой код был OGLES 1.x, затем OGLES 1.x и OGLES 2.x (без улучшения производительности), а теперь OGLES 2.x (улучшение рендеринга).
Эллис
1

Я бы порекомендовал иметь VBO с каждой вершиной, содержащей положение / вращение каждого визуализированного объекта и пакетирование на основе текстуры, как вы делаете. Я не очень знаком с ogl ES, поэтому я не уверен, какую версию glsl он поддерживает, но вы можете даже иметь возможность пакетировать на основе набора текстур и сохранять, какую из 4 или около того текстур вы передаете в вы будете использовать внутри вершины. Точечные спрайты определенно повысят вашу производительность, потому что это значительно сократит объем передаваемых данных, и пакетная обработка никогда не должна снижать производительность, если вы делаете это правильно. Кроме того, вы могли бы немного улучшить производительность, вычисляя вращение на шейдере и передавая только значение типа int / float в params или внутри самой вершины. (параметры будут быстрее,

sringer
источник
Спасибо за ваш ответ. Ваше предложение по вычислению вращения в шейдере превосходно, но, к сожалению, я использую OpenGL ES 1, который не поддерживает шейдеры, поэтому я застрял с фиксированным конвейером. Я попробую точечные спрайты, но я не могу использовать их во всех случаях, потому что есть верхний предел для их размера. Я все еще немного пессимистичен относительно VBO, если я пересчитываю положение каждой вершины каждого кадра, как VBO помогает?
user4241
это позволяет вашим данным о вершинах оставаться в графическом процессоре, что уменьшает объем данных, которые вы должны отправлять в графический процессор в каждом кадре. вам не нужны шейдеры, чтобы воспользоваться этим, вам вообще не нужно менять данные вершин, если у вас есть базовая позиция (например, начало координат) для каждого спрайта, вы можете просто изменить мировую матрицу с помощью это трансформация перед вызовом draw. Тем не менее, это может быть сложно при дозировании. используя фиксированную функцию, вероятно, было бы более выгодно просто переключиться на VBO и, по крайней мере, сейчас отказаться от пакетирования, что определенно даст вам импульс.
Спрингер
Я понимаю вашу точку зрения. В конце концов, вы не говорите о пакетировании, а просто используете один вызов для рисования одного игрового объекта. Я обязательно проверю, как VBO без пакетной обработки влияет на FPS в моей игре, но все равно 200 вызовов отрисовки на кадр кажутся слишком большими ... но я думаю, мне придется с этим смириться. Я приму ваш ответ, если другие ответы не появятся.
user4241
1

Вы упоминаете мобильные платформы, которые не имеют экземпляров. Но у вас все еще есть вершинные шейдеры, не так ли?

В этом случае вы все равно можете выполнять псевдоинкестирование, что тоже очень быстро. Создайте VBO (GL_STATIC_DRAW) с угловыми точками (относительно центральной точки спрайта, например, -1 / -1, 1 / -1, 1/1, -1/1) и любыми нужными вам текстурными координатами. ,
Затем установите один из общих атрибутов вершины для каждого вызова отрисовки в центральной точке спрайта и нарисуйте два треугольника с ограниченным буфером. Внутри вершинного шейдера прочитайте общий атрибут вершины и добавьте координаты вершины.

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

dm.skt
источник
Это звучит как хорошее решение для OpenGL ES 2.0. К сожалению, я использую ES 1, который вообще не имеет шейдеров.
user4241
0

Проблема заключается в количестве данных, которые вы отправляете в GPU каждый кадр. Просто создайте VBO для каждого пакета и заполните его один раз, затем примените соответствующие матрицы преобразования (через glMultMatrix или шейдер, если вы используете ES 2.0) при рисовании пакетов.

r2d2rigo
источник
Я не понимаю, как это помогает, когда у меня есть 200 отдельных игровых объектов с уникальными трансформациями? Использование glMultMatrix применило бы одинаковое преобразование ко всем объектам, а это не то, что я хочу. Кроме того, отправка данных в графический процессор не является узким местом; если я уберу преобразования на стороне процессора, то производительность очень хорошая.
user4241
Да, но VBO может улучшить производительность при правильном применении. Как вы сейчас визуализируете свои 200 объектов? Вы используете glBegin / glEnd?
TheBuzzSaw
1
Я использую движок Irrlicht 3D с пользовательским узлом сцены, поэтому я не использую OpenGL напрямую (но, полагаю, он использует простой glBegin / glEnd в данном случае). Действительно ли VBO поможет, так как мне придется изменять весь буфер каждый кадр? Кроме того, это не решает фундаментальную проблему привязки к процессору из-за вычислений вершинного преобразования. Но все равно спасибо за ваши ответы!
user4241