Как SimCity умещал симуляцию целого города в 16 МГц
Современное железо избаловало нас: даже в самых дешёвых процессорах сегодня минимум четыре ядра и миллиарды операций в секунду. Сама мысль, что серьёзная программа могла работать на частоте меньше гигагерца, кажется выдумкой. Между тем целые игры когда-то жили на оборудовании, которое было в тысячу раз слабее.
Когда вспоминают технические подвиги ретро-игр, обычно называют графические прорывы вроде Doom или Quake. Автор же куда больше впечатлён ранними градостроительными симуляторами: моделировать экономику целого города при ничтожных вычислительных ресурсах кажется немыслимым, ведь даже современные builder-игры порой с трудом тянут на мощных машинах.
386SX на 16 МГц
Оригинальный SimCity указывал в системных требованиях процессор Intel 386SX, работавший на частоте 16 МГц. (Автор отмечает, что в ранней версии заметки ошибочно назвал 12,5 МГц — это была тактовая частота базовой версии чипа, а не варианта SX.) Как же Maxis удалось запустить полноценную симуляцию города на таком железе, когда сегодня даже примитивным idle-играм нужны гигагерцы?
Причин много, и большинство из них внешние — экосистема ОС и прочее. Но в самом коде игры есть и остроумные оптимизации. К счастью, исходники SimCity были открыты под лицензией GPL в 2008 году под именем Micropolis (открыли только код, но не торговую марку, отсюда и смена названия), так что всё можно разобрать досконально.
Один кадр — не вся симуляция
Ядро симуляции сосредоточено в единственной функции Simulate() в файле s_sim.c. Она вызывается каждый кадр и дёргает обновления отдельных систем — преступности, пожаров, налогов и так далее. Беда в том, что выполнять их все в каждом кадре процессор 1989 года просто не вытянул бы.
Решение элегантно: SimCity попросту пропускает большую часть симуляции. У функции есть параметр mod16, который перебирает значения от 0 до 15 и определяет, какая именно часть симуляции будет посчитана в данном кадре. Нагрузка размазывается по шестнадцати кадрам.
Как это сказывается на игре? Практически никак. В среднем реакция на действие игрока наступает через восемь кадров, в худшем случае — через пятнадцать. Для шутера такой input lag был бы катастрофой, но для градостроительного симулятора, по мнению автора, плавная реакция даже приятнее мгновенной.
Приём жив до сих пор
Казалось бы, трюк специфичен для одного жанра, но на деле он лежит в основе множества оптимизаций. В гоночной Assetto Corsa (2014) отражения на кузовах машин рисуются через кубические карты, и обновлять все шесть граней каждый кадр слишком дорого — в конфиге прячется переменная FACES_PER_FRAME, задающая, сколько граней обновлять за раз.
Идти можно и дальше: определять объём работы в каждом кадре динамически — по размеру экрана или времени с прошлого обновления. Именно так работает Lumen в Unreal: он не пересчитывает освещение целиком каждый кадр, что объясняет и его красоту, и редкие странности с недообновлёнными отражениями.
Вывод прост. Садясь за игровой код, стоит спросить себя: действительно ли всё это нужно выполнять в каждом кадре?
Источник: Lobsters
Комментарии
Войдите, чтобы комментировать.