Я пишу свой собственный клон Minecraft (также написан на Java). Это прекрасно работает прямо сейчас. При расстоянии просмотра 40 метров я могу легко набрать 60 кадров в секунду на своем MacBook Pro 8,1. (Intel i5 + Intel HD Graphics 3000). Но если я поставлю расстояние просмотра на 70 метров, я достигну только 15-25 FPS. В реальном Minecraft, я могу поставить расстояние просмотра далеко (= 256 м) без проблем. Итак, мой вопрос: что я должен сделать, чтобы улучшить мою игру?
Оптимизации, которые я реализовал:
- Храните в памяти только локальные фрагменты (в зависимости от расстояния просмотра игрока)
- Отбор Frustum (сначала на куски, затем на блоки)
- Только рисование реально видимых граней блоков
- Использование списков на блок, которые содержат видимые блоки. Куски, которые становятся видимыми, добавят себя в этот список. Если они становятся невидимыми, они автоматически удаляются из этого списка. Блоки становятся (не) видимыми при строительстве или уничтожении соседнего блока.
- Использование списков на блок, которые содержат блоки обновления. Тот же механизм, что и в видимых списках блоков.
- Не используйте почти никаких
new
утверждений внутри игрового цикла. (Моя игра длится около 20 секунд, пока не будет вызван сборщик мусора) - Я использую списки вызовов OpenGL на данный момент. (
glNewList()
,glEndList()
,glCallList()
) Для каждой стороны своего рода блока.
В настоящее время я даже не использую какую-либо систему освещения. Я уже слышал о VBO. Но я не знаю точно, что это. Тем не менее, я сделаю некоторые исследования о них. Будут ли они улучшить производительность? Перед реализацией VBO, я хочу попытаться использовать glCallLists()
и передать список списков вызовов. Вместо этого, используя тысячу разglCallList()
. (Я хочу попробовать это, потому что я думаю, что настоящий MineCraft не использует VBO. Верно?)
Существуют ли другие приемы для повышения производительности?
Профилирование VisualVM показало мне это (профилирование только для 33 кадров при расстоянии просмотра 70 метров):
Профилирование с 40 метров (246 кадров):
Заметка: я синхронизирую много методов и блоков кода, потому что я генерирую куски в другом потоке. Я думаю, что получение блокировки для объекта - это проблема производительности, когда вы делаете это в игровом цикле (конечно, я говорю о том времени, когда есть только игровой цикл и новые порции не генерируются). Это правильно?
Редактировать: после удаления некоторых synchronised
блоков и некоторых других небольших улучшений. Производительность уже намного лучше. Вот мои новые результаты профилирования с 70 метров:
Я думаю, что довольно ясно, что selectVisibleBlocks
проблема здесь.
Заранее спасибо!
Мартейн
Обновление : после некоторых дополнительных улучшений (таких как использование циклов for вместо каждого, буферизация переменных вне циклов и т. Д.), Я теперь могу довольно хорошо пробежать расстояние просмотра 60.
Я думаю, что я собираюсь реализовать VBO как можно скорее.
PS: Весь исходный код доступен на GitHub:
https://github.com/mcourteaux/CraftMania
источник
Ответы:
Вы упомянули прорезание усеченного контура на отдельных блоках - попробуйте выбросить это. Большинство фрагментов рендеринга должны быть либо полностью видимыми, либо полностью невидимыми.
Minecraft восстанавливает только список отображения / буфер вершин (я не знаю, какой он использует), когда блок модифицируется в данном чанке, и я тоже . Если вы изменяете список отображения каждый раз, когда меняется представление, вы не получаете преимущества от списков отображения.
Кроме того, вы, кажется, используете куски мировой высоты. Обратите внимание, что Minecraft использует кубические блоки 16 × 16 × 16 для своих списков отображения, в отличие от загрузки и сохранения. Если вы сделаете это, есть еще меньше причин для того, чтобы отбирать отдельные куски.
(Примечание: я не изучал код Minecraft. Вся эта информация является либо слухом, либо моими собственными выводами из наблюдения за рендерингом Minecraft во время игры.)
Более общий совет:
Помните, что ваш рендеринг выполняется на двух процессорах: CPU и GPU. Если ваша частота кадров недостаточна, то один или другой является ограничивающим ресурсом - ваша программа либо связана с процессором, либо с графическим процессором (при условии, что она не обменивается или не имеет проблем с расписанием).
Если ваша программа работает на 100% ЦП (и не имеет неограниченной другой задачи для выполнения), то ваш ЦП выполняет слишком много работы. Вы должны попытаться упростить его задачу (например, меньше отбраковывать) в обмен на то, чтобы графический процессор делал больше. Я сильно подозреваю, что это ваша проблема, учитывая ваше описание.
С другой стороны, если GPU является пределом (к сожалению, обычно нет удобных мониторов загрузки на 0% -100%), вам следует подумать о том, как отправить на него меньше данных или потребовать заполнить меньшее количество пикселей.
источник
Что так часто вызывает Vec3f.set? Если вы создаете то, что хотите рендерить с нуля, каждый кадр, то это определенно то место, где вы хотели бы начать ускорять его. Я не большой пользователь OpenGL, и я не знаю много о том, как Minecraft рендерит, но кажется, что математические функции, которые вы используете, убивают вас прямо сейчас (просто посмотрите, сколько времени вы проводите в них и сколько раз их зовут - смерть от тысячи порезов зовет их).
В идеале ваш мир должен быть сегментирован так, чтобы вы могли группировать вещи для рендеринга вместе, создавать объекты буфера вершин и повторно использовать их в нескольких кадрах. Вам нужно будет только изменить VBO, если мир, который он представляет, каким-то образом изменится (как пользователь его редактирует). Затем вы можете создавать / уничтожать VBO для того, что вы представляете, так как оно становится видимым, чтобы снизить потребление памяти, вы будете принимать удар только тогда, когда VBO был создан, а не каждый кадр.
Если в вашем профиле правильно указано количество вызовов, вы звоните очень много раз. (10 миллионов звонков на Vec3f.set ... ой!)
источник
Мое описание (из моих собственных экспериментов) здесь применимо:
Что для более эффективного рендеринга вокселов: готовый VBO или геометрический шейдер?
Minecraft и ваш код, вероятно, используют конвейер с фиксированной функцией; мои собственные усилия были с GLSL, но суть в целом применима, я чувствую:
(Из памяти) Я сделал усечку, которая была на полблока больше, чем усеченность экрана. Затем я проверил центральные точки каждого блока (у майнкрафта есть 16 * 16 * 128 блоков ).
Грани в каждом из них имеют промежутки в массиве элементов VBO (многие грани кусков разделяют одно и то же VBO до тех пор, пока оно не будет «полным»; думайте как
malloc
; те, у которых одинаковая текстура в том же VBO, где это возможно) и индексы вершин для севера грани, южные грани и т. д. являются смежными, а не смешанными. Когда я рисую, я делаюglDrawRangeElements
для северных граней, с нормой, уже спроектированной и нормализованной, в униформе. Затем я делаю южные грани и так далее, поэтому нормалей нет ни в одном VBO. Для каждого фрагмента мне нужно только испускать видимые грани - например, только в центре экрана нужно рисовать левую и правую стороны, например; это простоGL_CULL_FACE
на уровне приложения.Самым большим ускорением, iirc, было отбраковка внутренних граней при полигонизации каждого куска.
Также важно управление текстурным атласом и сортировка граней по текстуре и помещение граней с той же текстурой в ту же VBO, что и у других кусков. Вы хотите избежать слишком большого количества изменений текстуры и сортировки граней по текстуре и т. Д., Чтобы минимизировать количество пролетов в
glDrawRangeElements
. Объединение смежных граней одной плитки в большие прямоугольники также было большой проблемой. Я говорю о слиянии в другом ответе, приведенном выше.Очевидно, что вы полигонизируете только те фрагменты, которые когда-либо были видны, вы можете отбросить те фрагменты, которые не были видны в течение длительного времени, и повторно полигонизировать отредактированные фрагменты (так как это редкое явление по сравнению с их рендерингом).
источник
Откуда все ваши сравнения (
BlockDistanceComparator
)? Если это из функции сортировки, может ли она быть заменена на сортировку по основанию (которая асимптотически быстрее, а не основана на сравнении)?Если посмотреть на время, даже если сама сортировка не так уж и плоха, ваша
relativeToOrigin
функция вызывается дважды для каждойcompare
функции; все эти данные должны быть рассчитаны один раз. Должна быть быстрее сортировка вспомогательной структуры, напримера затем в псевдокоде
Извините, если это неверная структура Java (я не касался Java с тех пор, как учился в старших классах), но, надеюсь, вы поняли идею.
источник
Да, используйте VBO и CULL, но это касается практически каждого игры. То, что вы хотите сделать, это только визуализировать куб, если он виден игроку, И если блоки касаются особым образом (скажем, кусок, который вы не видите, потому что он подземный), вы добавляете вершины блоков и делаете это почти как «больший блок», или в вашем случае - кусок. Это называется жадной сеткой, и это резко повышает производительность. Я занимаюсь разработкой игры (на основе вокселей), и в ней используется алгоритм жадных сеток.
Вместо рендеринга все так:
Это делает это так:
Недостатком этого является то, что вам нужно делать больше вычислений на чанк при первоначальной сборке мира, или если игрок удаляет / добавляет блок.
практически любой тип воксельного движка нуждается в этом для хорошей производительности.
То, что он делает, проверяет, касается ли грань блока другой грани блока, и если да, то визуализирует только как одну (или нулевую) грань блока. Это очень дорогое прикосновение, когда вы рендерите куски очень быстро.
источник
Казалось бы, ваш код тонет в объектах и вызовах функций. Измеряя числа, не похоже, что происходит какая-то внутренняя связь.
Вы можете попытаться найти другую среду Java или просто поработать с настройками той, которая у вас есть, но простой и простой способ сделать ваш код не быстрым, а гораздо менее медленным - по крайней мере, внутренне в Vec3f остановить Кодирование ООО *. Сделайте каждый метод самодостаточным, не вызывайте какие-либо другие методы только для выполнения некоторой простой задачи.
Изменить: Несмотря на то, что повсюду есть издержки, может показаться, что упорядочение блоков перед рендерингом - это наихудшая производительность. Это действительно необходимо? Если это так, то вам, вероятно, следует начать с прохождения цикла и вычислить расстояние до каждого блока до начала координат, а затем отсортировать по нему.
* Слишком объектно-ориентированный
источник
Вы также можете попытаться разбить математические операции до побитовых операторов. Если у вас есть
128 / 16
, попробуйте сделать оператор побитового:128 << 4
. Это очень поможет с вашими проблемами. Не пытайтесь заставить вещи работать на полной скорости. Обновляйте свою игру со скоростью 60 или около того, и даже разбивайте ее на другие вещи, но вам придется уничтожать или размещать воксели, или вам нужно составить список задач, который снизит ваш fps. Вы можете сделать частоту обновления около 20 для юридических лиц. И что-то вроде 10 для мировых обновлений и / или поколения.источник