Наконец-то я доделал игру, которая работает на видеокарте. Она несколько месяцев повисела в раннем доступе на стиме, и теперь я её окончательно выпустил. Основная фишка игры в том, что она представляет собой физическую симуляцию, которая выполняется на графическом процессоре. Основной код игры — это огромный compute shader, 6 тысяч строк на HLSL. Десятки тысяч взаимодействующих частиц обрабатываются параллельно, и выходит довольно быстро. Всё в игре сделано из этих частиц. Вот несколько гифок о том, как это работает:
Хочется просуммировать опыт разработки такого рода игры. Какие достоинства и недостатки у вычислений на видеокарте.
Достоинства:
1. Производительность GPU в 10-100 раз выше, чем у процессора, когда дело касается параллельных вычислений. Это очень много, так что на видеокарте можно делать принципиально другие игры, чем на процессоре. Моя игра просто не работала бы на CPU (то есть, была бы слишком медленной).
Недостатки:
1. Мало туториалов. Я потратил довольно много времени, чтобы всё изучить. И ещё больше — чтобы решить возникавшие проблемы.
2. Это в принципе сложнее, программировать для графического ядра. Привычные для процессорного вычисления вещи могут не работать. Например, вместо трёхмерного цикла на трёхмерном массиве я должен был организовать трёхмерное пространство потоков, одновременно обрабатывающих одномерный массив (в который пришлось разложить исходный трёхмерный). Кроме того, надо заботиться, чтобы параллельные потоки работали с общими данными в защищённом режиме. В целом всё это решаемо, но требует больше времени.
3. Проблемы возникли и при чтении данных из видеопамяти. Это приходилось делать на каждом цикле, но работало оно слишком медленно. Потоу что в юнити не было асинхронного чтения в удобное для графического конвейра время, и он постоянно блокировался при чтении данных. В итоге fps падал вдвое. Пришлось использовать нативный плагин для асинхронного чтения из видеопамяти средствами directX, но во-первых, это не работало вне windows, а во-вторых, по неизвестным причинам это не работало на некоторых видеокартах, игроки жаловались.
4. Не у всех есть достаточно современные видеокарты, поддерживающие шейдеры и обладающие достаточной производительностью. Это ограничивало круг игроков, способных поиграть в мою игру. А те, у кого тормозило, были не прочь написать в Стиме негативный отзыв.
5. Графические API на разных платформах немного отличаются друг от друга. В простых случаях они совместимы со стандартом directX, но у меня — не простой случай, я приблизился ко многим лимитам вплотную. К примеру, в DX11 один кернел может работать только с восемью буфферами. А андроидный API — только с четырьмя. У Metal тоже свои ограничения, вроде отсутствия защищённой записи в общую для потоков текстуру, у Vulkan — ещё какие-то ограничения. В итоге, игра работает только на Windows.
6. Не удалось сделать вычисления детерминированными, так что пришлось обойтись без мультиплеера. Хотя, теоретически понятно: надо всё сделать на int-ах, и результаты на каждом цикле не должны зависеть от очерёдности потоков (которая может отличаться от раза к разу в одинаковых условиях). Но мои эксперименты пока не привели к детерминированности вычислений.
Но по итогам, я всё-таки доволен, что сделал что хотел.
Как игроку мне очень интересно было посмотреть, каково это — поиграть в игре, полностью физичной, чтоб всё было разрушаемо. Возможно, в будущем сделаю попытку что-то подобное изобразить в 3д.
Как гейм-дизайнеру, было интересно поиграть с физикой, попытаться создать уникальный геймплей, невозможный вне физической симуляции.
А как программисту было полезно приобрести навык вычислений на GPU. Думаю, в будущем этого будет больше, слишком уж хороший выигрыш в производительности. Хотя, было бы неплохо для начала, если б все графические API были совместимы со стандартом directx11. Вовсе не обязательно делать на GPU целую игру, можно перенести некоторые части игры на обработку в видеопроцессор. Например, поиск пути в стратегиях. А сейчас не любой разработчик будет готов писать compute shader, если всё равно его придётся дублировать, на случай, если игра запущена на платформе, не поддерживающей вычисления на видеокарте.
Ну и если кто-то вдруг захочет попробовать свои силы в этой области, вот парочка туториалов, они мне в своё время открыли путь в эту область:
Первый
Второй
Хочется просуммировать опыт разработки такого рода игры. Какие достоинства и недостатки у вычислений на видеокарте.
Достоинства:
1. Производительность GPU в 10-100 раз выше, чем у процессора, когда дело касается параллельных вычислений. Это очень много, так что на видеокарте можно делать принципиально другие игры, чем на процессоре. Моя игра просто не работала бы на CPU (то есть, была бы слишком медленной).
Недостатки:
1. Мало туториалов. Я потратил довольно много времени, чтобы всё изучить. И ещё больше — чтобы решить возникавшие проблемы.
2. Это в принципе сложнее, программировать для графического ядра. Привычные для процессорного вычисления вещи могут не работать. Например, вместо трёхмерного цикла на трёхмерном массиве я должен был организовать трёхмерное пространство потоков, одновременно обрабатывающих одномерный массив (в который пришлось разложить исходный трёхмерный). Кроме того, надо заботиться, чтобы параллельные потоки работали с общими данными в защищённом режиме. В целом всё это решаемо, но требует больше времени.
3. Проблемы возникли и при чтении данных из видеопамяти. Это приходилось делать на каждом цикле, но работало оно слишком медленно. Потоу что в юнити не было асинхронного чтения в удобное для графического конвейра время, и он постоянно блокировался при чтении данных. В итоге fps падал вдвое. Пришлось использовать нативный плагин для асинхронного чтения из видеопамяти средствами directX, но во-первых, это не работало вне windows, а во-вторых, по неизвестным причинам это не работало на некоторых видеокартах, игроки жаловались.
4. Не у всех есть достаточно современные видеокарты, поддерживающие шейдеры и обладающие достаточной производительностью. Это ограничивало круг игроков, способных поиграть в мою игру. А те, у кого тормозило, были не прочь написать в Стиме негативный отзыв.
5. Графические API на разных платформах немного отличаются друг от друга. В простых случаях они совместимы со стандартом directX, но у меня — не простой случай, я приблизился ко многим лимитам вплотную. К примеру, в DX11 один кернел может работать только с восемью буфферами. А андроидный API — только с четырьмя. У Metal тоже свои ограничения, вроде отсутствия защищённой записи в общую для потоков текстуру, у Vulkan — ещё какие-то ограничения. В итоге, игра работает только на Windows.
6. Не удалось сделать вычисления детерминированными, так что пришлось обойтись без мультиплеера. Хотя, теоретически понятно: надо всё сделать на int-ах, и результаты на каждом цикле не должны зависеть от очерёдности потоков (которая может отличаться от раза к разу в одинаковых условиях). Но мои эксперименты пока не привели к детерминированности вычислений.
Но по итогам, я всё-таки доволен, что сделал что хотел.
Как игроку мне очень интересно было посмотреть, каково это — поиграть в игре, полностью физичной, чтоб всё было разрушаемо. Возможно, в будущем сделаю попытку что-то подобное изобразить в 3д.
Как гейм-дизайнеру, было интересно поиграть с физикой, попытаться создать уникальный геймплей, невозможный вне физической симуляции.
А как программисту было полезно приобрести навык вычислений на GPU. Думаю, в будущем этого будет больше, слишком уж хороший выигрыш в производительности. Хотя, было бы неплохо для начала, если б все графические API были совместимы со стандартом directx11. Вовсе не обязательно делать на GPU целую игру, можно перенести некоторые части игры на обработку в видеопроцессор. Например, поиск пути в стратегиях. А сейчас не любой разработчик будет готов писать compute shader, если всё равно его придётся дублировать, на случай, если игра запущена на платформе, не поддерживающей вычисления на видеокарте.
Ну и если кто-то вдруг захочет попробовать свои силы в этой области, вот парочка туториалов, они мне в своё время открыли путь в эту область:
Первый
Второй