- PVSM.RU - https://www.pvsm.ru -
В прошлом году в рамках празднования 30-летней годовщины Doom мы продемонстрировали его порт для платы Sparkfun Thing Plus Matter MGM240P. В этом году мы портируем Quake на плату Arduino Nano Matter.
Порт не только включал в себя все оригинальные элементы Doom, например, звуки и музыку, но и имел мультиплеер на основе BLE, а также работал с частотой 35 FPS c улучшенным разрешением 320 x 240 пикселей (разрешение оригинала — 320x200 пикселей).
Изображение 1. Мультиплеер Doom на плате Sparkfun Thing Plus Matter MGM240P
Как понятно из названия, основой платы Sparkfun Thing Plus Matter MGM240P стал модуль Silicon Labs MGM240P, созданный на основе системы на чипе (SoC) EFR32MG24. Наряду с радиоканалом на 2,4 ГГц SoC EFR32MG24 имеет ядро Cortex M33 на 80 МГц с 1,5 МБ флэш-памяти и 256 КБ ОЗУ, благодаря чему он становится идеальным выбором для использования с Matter.
Несмотря на то, что Doom требовал 4 МБ ОЗУ, порт доказал, что 256 КБ ОЗУ вполне достаточно: их хватило не только для работы всего движка, но и для обработки стека BLE и увеличения разрешения на 20%.
Doom уже портировали на множество устройств и микроконтроллеров, поэтому мы решили, что на этот раз стоит взяться за задачу потруднее. Мы хотели найти более сложную игру, требующую больше ОЗУ, с красивой графикой и звуком, и реализовать её порт.
После Doom вышло множество других шутеров от первого лица, но они не обеспечили достаточный объём технических улучшений, чтобы считать их «истинным потомком Doom», хотя в некоторые из них было очень интересно играть. Всего через три года после Doom вышел Quake, совершивший огромный шаг в технологиях, сравнимый с переходом от Wolfstein 3D к Doom.
В Quake был реализован полностью трёхмерный мир, позволяющий создавать произвольную геометрию карт с мостами, множеством этажей, наклонными поверхностями и динамическими точечными эффектами освещения. Более того, враги, оружие и бонусы теперь стали полностью трёхмерными текстурированными полигональными объектами, в отличие от Doom, где они были простыми 2D-спрайтами. Такие 3D-объекты могли похвастаться динамическим освещением по Гуро, повышавшим реализм. Кроме того, в игру был добавлен движок частиц для реализации дыма, взрывов и так далее.
Изображение 2. В Quake бонусы, оружие и враги были текстурированными 3D-объектами. В Doom они были 2D-спрайтами.
Изображение 3. Quake появилось динамическое и статическое освещение
Изображение 4. Игрок может ходить и по мосту, и под ним. В Doom это было невозможно.
В отличие от Doom, в Quake имелся движок скриптинга, позволявший моддерам модифицировать поведение монстров, оружия и мира.
Но за эти улучшения приходилось расплачиваться. Например, Doom с приличной скоростью мог работать на 486DX с 33 МГц и 4 МБ ОЗУ (согласно https://thandor.net/benchmark/32 [1], достигая в demo 3 средней частоты кадров 18 FPS). На такой же конфигурации Quake просто не запустился бы.
Если увеличить ОЗУ до 8 МБ, то на той же машине Quake работал бы, как слайд-шоу, с частотой 3,7 FPS (см. https://thandor.net/benchmark/33 [2]). Для запуска Quake с той же средней частотой 18 FPS и разрешением требовался CPU класса Pentium.
8 МБ ОЗУ? Процессор класса Pentium? Звучит само по себе сложно, особенно учитывая, что ОЗУ у нас примерно в тридцать раз меньше!
Вкратце перечислим цели нашей новой сложной задачи:
Также есть необязательные функции, которые, однако, хотелось бы реализовать.
Ещё есть функции, совершенно не входящие в наш объём работ:
Что допускается при решении задачи:
Поддерживается два типа оборудования. Первый — это прошлогодняя плата, благодаря которой мы запустили Doom на плате Sparkfun Thing Plus Matter MGM240P. Единственное отличие здесь будет заключаться в модулях флэш-памяти: мы заменили их на две интегральные схемы по 16 МБ каждая, получив 32 МБ.
Изображение 5. Одна из плат, которые мы использовали для запуска Doom. После замены схем флэш-памяти на модули по 16 МБ она сможет запускать и Quake!
Второй тип совершенно новый, он изготовлен на основе платы Arduino Nano Matter. Мы поднапрягли фантазию и назвали эту плату «The Gamepad». В посте будет рассказываться о ней.
Мы дополнили предыдущую конструкцию, добавив ещё восемь кнопок (теперь их стало 16), два аналоговых джойстика и два звукоусилителя Class-D.
Изображение 6. Новая плата The Gamepad в формате геймпада с дополнительными кнопками и двумя аналоговыми стиками
Изображение 7. Обратная сторона платы.
Обе платы спроектированы только из монтируемых в отверстия компонентов, так что устройство может воспроизвести даже новичок. Недостатком этого стала более высокая цена по сравнению с использованием SMD-компонентов.
Схема «The Gamepad»:
Все файлы KiCAD выложены в репозиторий Github: https://github.com/next-hack/TheGamepadDesignFiles [4]
Наш порт мы разрабатывали на основе кодовой базы SDLQuake1.09 (https://www.libsdl.org/projects/quake/) — порта WinQuake на Simple Direct-media Layer Library: единственное отличие от исходников оригинала заключается в нескольких дополнительных файлах для поддержки видео и аудио SDL.
Мы выбрали эту кодовую базу для удобства разработки, оптимизации и тестирования на современной машине с Windows. Снизив потребление памяти до нужного нам, мы начали портирование на целевые платформы.
Должны признать, что по сравнению с этим проектом портирование Doom на MG240 было детской забавой.
Ниже приведён неполный список проблем, с которыми мы столкнулись. Более подробный список проблем и их решений можно посмотреть в полной статье на next-hack.com [5]:
Таким образом, требуется много ОЗУ, даже если мы сохраним/оставим всё неизменяемое во внешней/внутренней флэш-памяти. Если просто суммировать всё перечисленное выше, то мы получим примерно 1,4 МБ. И это даже без учёта всего остального: данных и буферов звуковых каналов, клиент-серверных буферов, буфера консоли, буфера клавиш, переменных консоли, консольных команд, данных частиц и, самое главное — данных моделей, которые могут увеличить общий размер используемой памяти ещё на несколько мегабайтов.
«Простых» оптимизаций памяти, которые мы реализовали для запуска Doom, было недостаточно; подробнее об этом мы написали в статье на next-hack.com [5].
В версии для PC и в портах Quake все данные доступны из ОЗУ (а то и из кэша данных CPU), которое даже в 1996 году имело достаточно большую пропускную способность и низкие задержки. На самом деле, пропускная способность последовательного чтения сильно варьировалась, но для 64-битной EDO DRAM на 40 МГц (уже продававшейся в 1996 году) можно было достичь максимальной пропускной способности в 320 МБ/с. Задержки произвольных операций чтения находились в интервале 110-130 нс [6].
У нас же было всего 276 КБ ОЗУ и 1,5 МБ флэш-памяти. В нашем чипе есть относительно большой кэш команд, но нет кэша данных. Считывание данных из внутренней флэш-памяти всегда вызывает по крайней мере один такт ожидания. В случае невыровненного доступа время ожидания может увеличиться. Максимальная скорость последовательного считывания данных составляет 181 МБ/с в случае разгона, при этом во внутренней флэш-памяти можно сохранить только ограниченный объём данных, поэтому мы должны отдавать приоритет геометрическим данным (координатам вершин, BSP-дереву и так далее). Всё остальное, включая текстуры и звук, грузится с внешних интегральных схем флэш-памяти SPI, имеющих более низкую пропускную способность (суммарно максимум 17 МБ/с) и очень большие задержки (порядка нескольких микросекунд).
Более того, в этом порте мы не можем использовать двойную буферизацию (как мы делали в Doom), потому что это займёт слишком много памяти. Это снижает производительность, потому что нам приходится ждать, пока DMA отправит n-ную строку, прежде чем мы сможем её модифицировать. Кроме того, у нас нет ОЗУ для кэша поверхностей, поэтому нам приходится вычислять освещение текстур на лету.
Чтобы частично решить эти проблемы, мы разогнали MCU. Выяснилось, что MGM240 нормально работает на частоте более 136 МГц (может и быстрее, но мы решили немного подстраховаться, чтобы стабильность не зависела от конкретного устройства). Разумеется, Silicon labs не рекомендует заниматься разгоном (как и все производители интегральных схем), но наш проект не связан с безопасностью и от него не зависят жизни, это всего лишь игра. Кроме того, разгон MCU достаточно часто применяется в любительских и хакерских проектах (например, в порте Doom для RP2040 Kilograham разогнал двухъядерный Cortex M0+ с 133 до 270 МГц).
Разгон обеспечил нам рост производительности в 1,7 раза, но до играбельности оставалось по-прежнему очень далеко: частота кадров находилась в интервале 7-10 FPS.
Нам нужны были стратегии оптимизации. Среди прочего мы выбрали:
После таких улучшений частота кадров увеличилась в 2,5-4,5 раза (4,25-7,7 раза, если учитывать решение без разгона). Теперь частота кадров колеблется от 17,7 до 45,6 FPS (timedemo для demo3 имеет со включенным звуком оценку 28,0 FPS).
Для этого порта нам пришлось разработать ещё четыре инструмента, выполняющие следующие функции:
Все четыре инструмента реализованы как консольные приложения Windows, использующие Code::Blocks. Их исходники включены в репозиторий Github [7], но код нужно сильно подчистить.
Чтобы играть в Quake, обязательно нужно использовать конвертер Quake Pak, преобразующий shareware-файл pak0.pak. Преобразованный файл нужно переименовать в pak0.pak и записать на карту microSD, чтобы загрузить во внешнюю флэш-память, следуя командам на экране. Остальные три инструмента добавлены только для справки.
Запущенный порт и полное видео можно посмотреть на YouTube [8]!
Изображение 8. Первые кадры demo 3. Полностью реализовано воспроизведение демо.
Изображение 9. Консоль реализована, её можно активировать из меню Options. Мы вставили в качестве чита «showfps 1», чтобы можно было выводить в левую нижнюю часть экрана текущую частоту кадров.
Изображение 10. Начальная карта, комната выбора эпизода. Поддерживается только первый эпизод.
Изображение 11. Первый уровень. Частота кадров сильно скачет, но первоначально равна 27 FPS.
Изображение 12. В маленьких помещениях частота кадров превышает 30 FPS.
Изображение 13. На открытых пространствах частота кадров падает (в этом случае 20,8 FPS).
Изображение 14. В некоторых случаях частота кадров снова превышает 30 FPS.
Наряду со множеством портов/улучшений Quake на PC/MAC существует несколько портов Quake на микроконтроллеры. Например:
1) Порт на RISC-V: https://www.elektormagazine.com/articles/start-playfully-with-riscv [9]
2) Порт на Cortex M7 MCU: https://github.com/FantomJAC/quakembd [10]
Однако в этих системах было доступно как минимум 8 МБ ОЗУ и они имели достаточно мощные CPU (двухъядерный RISC-V на 400 МГц и суперскалярный Cortex M7 на 480 МГц), а значит, основные усилия, вероятно, были приложены к разработке слоя аппаратной абстракции, без необходимости радикального изменения исходников.
Судя по видео, эти порты Quake работают очень быстро. Это вполне ожидаемо, учитывая большой объём ОЗУ и вычислительную мощь.
Гораздо более впечатляющую работу проделал для GBA Рэнди Линден: https://www.xda-developers.com/how-quake-ported-game-boy-advance/ [11].
Несмотря на гораздо меньшую скорость и разрешение всего 120x160 пикселей (GBA имеет разрешение 240x160, но в порте каждый 3D-пиксель состоит из двух горизонтальных пикселей), программисту удалось частично запустить Quake всего на 384 КБ ОЗУ и ARM 7TDMI с частотой 16,7 МГц. Производительность, которой Рэнди удалось достичь благодаря сотням тысяч строк оптимизированного ассемблерного кода, просто потрясает.
Судя по статье и видео, порту Quake не хватает следующего (стоит отметить, что мы говорим о порте Quake, а не о производном от него Cyboid с расширенными возможностями, который также показан в статье и видео):
Изображение 15. Кадр из видео [12] Modern Vintage Gamer, в котором отображается первоначальная частота кадров в 9,5 FPS (частота кадров умножается на десять и указывается рядом с бронёй). Разрешение составляет всего 120x160, потому что каждый пиксель удвоен по горизонтали, а у освещения отсутствует билинейная фильтрация; впрочем, несмотря на эти ограничения, такая скорость на ARM7TDMI с 16,7 МГц — очень большое достижение.
Идентичное сравнение выполнять сложно. По сравнению с портом для GBA, наш работает быстрее, но оборудование GBA гораздо более медленное. Тем не менее, порт для GBA имеет гораздо меньшее разрешение, использует примерно на 40% больше ОЗУ и не реализовал все функции Quake.
С другой стороны, упомянутый выше порт для RISC-V работает гораздо быстрее нашего (в видео [13] частота составляет от 30 до 75 FPS), но устройство имеет 8 МБ ОЗУ и двухъядерный процессор RISC-V на 400 МГц (впрочем, мы полагаем, что использовано только одно ядро). Наличие такой большой памяти позволяет реализовать кэш поверхностей, а процессор RISC-V на 400 МГц примерно в 2-3 раза быстрее нашего разогнанного MCU. То же самое относится к порту для Cortex M7.
А что насчёт обычного PC? Наша оценка timedemo составила 28,0 FPS, что быстрее, чем на Pentium 100 МГц (26,7 FPS) и лучше, чем 6x86MX PR200 (27,4 FPS). Однако наши значения были получены с включенным звуком, а бенчмарки https://thandor.net/benchmark/33 [2] выполнялись с ключом -nosound.
Изображение 16. Часть таблицы результатов бенчмарка в https://thandor.net/benchmark/33 [2]. Наша оценка в 28,0 FPS выше, чем у Pentium 100 и 6x86MX PR200, но мы всё равно отстаём от Pentium 120 и от 6x86MX PR233.
Как показано в видео по ссылке, во время геймплея частота кадров иногда падает ниже 20 FPS, но основную часть времени она сильно выше этой границы, даже достигая очень высоких пиков, превышающих 30 FPS. Реальная частота кадров зависит от множества факторов, например, от сложности уровня и сцены (то есть от количества отрисовываемых поверхностей), количества видимых объектов, частиц и даже от количества одновременно активных аудиоканалов.
Мы считаем, что именно нехватка ОЗУ, не позволившая использовать систему кэширования поверхностей из оригинала, а также заставившая нас в каждом кадре загружать всю графику из внешней флэш-памяти, не дала нам достичь ещё большей частоты кадров. Кроме того, бенчмарки в https://thandor.net/benchmark/33 [2], вероятно, выполнены с кодом, содержащим ассемблерные подпрограммы, который, по словам Джона Кармака, вдвое быстрее кода на C (https://github.com/id-Software/Quake).
Мы считаем, что выполнили все требования, в том числе и «необязательные, но желательные»:
Изображение 17. Реализована система меню, а настройки сохраняются во внешнюю флэш-память.
Насколько мы знаем, этот порт Quake пока использует наименьший объём ОЗУ. Игра работает с вполне комфортной скоростью (в среднем бенчмарки показывают примерно 27 FPS, но реальное среднее зависит от сложности сцены и может быть выше или ниже), используя всего 276 КБ ОЗУ в системе, стоящей всего малую долю цены компьютера, который бы потребовался для Quake в 1996 году.
Изображение 18. В среднем частота кадров позволяет играть с удовольствием.
Мы считаем, что возможности для оптимизации по-прежнему есть, можно увеличить скорость, а если добавить больше флэш-памяти, то, возможно, удастся запустить и полную версию игры.
О да, на плате Arduino Nano Matter можно запустить и Doom. С мультиплеером через BLE и на полной скорости!
Изображение 19. Два устройства с запущенным Doom. Правое работает от аккумулятора.
Автор: PatientZero
Источник [14]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/doom/398894
Ссылки в тексте:
[1] https://thandor.net/benchmark/32: https://thandor.net/benchmark/32
[2] https://thandor.net/benchmark/33: https://thandor.net/benchmark/33
[3] https://www.silabs.com/documents/public/reference-manuals/efr32xg24-rm.pdf: https://www.silabs.com/documents/public/reference-manuals/efr32xg24-rm.pdf
[4] https://github.com/next-hack/TheGamepadDesignFiles: https://github.com/next-hack/TheGamepadDesignFiles
[5] next-hack.com: https://next-hack.com/index.php/2024/09/22/quake-port-to-sparkfun-and-arduino-nano-matter-boards-using-only-276-kb-ram/
[6] интервале 110-130 нс: https://www.alldatasheet.com/view.jsp?Searchword=MT4C4007-6S&q=MT4C4007-6
[7] репозиторий Github: https://github.com/next-hack/MG24Quake
[8] YouTube: https://get.silabs.com/youtube-quake-arduino
[9] https://www.elektormagazine.com/articles/start-playfully-with-riscv: https://www.elektormagazine.com/articles/start-playfully-with-riscv
[10] https://github.com/FantomJAC/quakembd: https://github.com/FantomJAC/quakembd
[11] https://www.xda-developers.com/how-quake-ported-game-boy-advance/: https://www.xda-developers.com/how-quake-ported-game-boy-advance/
[12] видео: https://www.youtube.com/watch?v=R43k-p9XdIk
[13] видео: https://www.youtube.com/watch?v=poBBrIWt_HE
[14] Источник: https://habr.com/ru/articles/848396/?utm_campaign=848396&utm_source=habrahabr&utm_medium=rss
Нажмите здесь для печати.