Разработка игрового 3D-движка Force Tech: загадочные тормоза

Прошедший месяц разработки прошел под эгидой исследования причин странных задержек при отрисовке кадра в моём движке Force Tech.

Чтобы читать было интереснее, предлагаю посмотреть на скриншоты из предыдущей версии Force Tech. FM для Thief II - Reverse Robbery Director's Cut.
Чтобы читать было интереснее, предлагаю посмотреть на скриншоты из предыдущей версии Force Tech. FM для Thief II - Reverse Robbery Director's Cut.

В какой-то момент я заметил, что от былых достижений в скорости отрисовки не осталось и следа: полтора миллиона полигонов выводились не в 90fps (~11ms), а в 30. Сначала я подумал, что что-то сломал. Долго всё перепроверял, оптимизировал, улучшал, в итоге подобными ухищрениями удалось ускорить отрисовку до 40 fps (~20ms), но это всё равно было в два раза медленнее прежнего результата. Чуть позже я случайно обнаружил, что былая скорость возвращается, если запустить приложение сразу после перезагрузки компьютера, но если сделать это хотя бы через пару минут простоя, тормоза снова одолевают.

Сначала думал, что причина — некий фоновый паразитный процесс, долго его искал, убивая в диспетчере задач всё что казалось подозрительным и не очень. В результате пришел к выводу, что дело не в процессе, а скорее в памяти. Мой движок начинал нормально работать, если суммарная занятая оперативная память в системе не превышала 4 Гигабайт. Это казалось очень странным, ведь, как минимум, раньше такого не было.

В профилировщике сравнил два варианта работоспособности (нормальный и тормозной) и увидел там странные задержки в видеодрайвере. Спросил умных ребят на форуме gamedev.ru, сначала думали на синхронизацию потоков CPU, но запуск даже в одном потоке не дал улучшений. Потом я по рекомендации решил уменьшить размер атласа текстур с 8k до 4k и произошло долгожданное чудо — скорость вернулась! Но было непонятно, почему. Принялся выяснять, для этого сделал то, о чём давно мечтал — массив текстурных атласов в видеопамяти. Оказалось, что при выделении определенного количества слоёв атласа в видеопамяти, всё нормально, но стоило выделить на один больше — начинались тормоза.

Thief Gold FM - A Thief's Training
Thief Gold FM - A Thief's Training

Виновницей подобных задержек была назначена нехватка видеопамяти. Если честно, я думал, что при выделении слишком большого объема видеопамяти драйвер видеокарты сообщит, что эта самая память закончилась, мол увы, приложение завершает работу. Или нечто подобное. Всё оказалось хитрее: при нехватке видеопамяти, драйвер прозрачно начинает пользоваться оперативной, а это приводит к синхронизации видеокарты и CPU, что означает постоянные простои обоих устройств в ожидании друг друга — тормоза.

Пользуясь случаем, хочу поблагодарить всех причастных ребят с форума gamedev.ru, в особенности, пользователя Aroch! Спасибо!

"Но, почему видеопамять так быстро закончилась?" — спросите вы. Такой же вопрос возник и у участников форума. На самом деле, я же делаю "стриминг всего", геометрии и текстур, для этого в видеопамяти в начале разработки умозрительно выделил по 128/256 Мб для каждого из буферов (всего их аж 10 штук). Но когда решил подсчитать, сколько получилось суммарно, понял что почти приблизился к лимиту своей видеокарты в 2Гб. Вооружившись пониманием происходящего, изменил лимиты таким образом, чтобы не выходить за пределы Гигабайта видеопамяти, и при этом оставался бы разумный запас для каждого буфера.

"Тогда надо получить всю имеющуюся свободную видеопамять и выделить её приложению!" — подумал я, — "чтобы не вылезать за её пределы и у пользователей не начинались необъяснимые тормоза!" Но мой пыл быстро угас, когда после продолжительных поисков и исследований так и не нашлось кроссплатформенного достоверного способа узнать доступные объемы памяти видеокарты. Да, есть монструозные по объемам, либо же платные библиотеки, которые умеют такое делать, но я решил сделать это одной из настроек программы, специфический «ползунок качества картинки».

Казалось бы, вот и сказке конец, да в ней намёк, который добрые молодцы могут смело мотать на ус... Но я в тот момент настолько приблизился к реализации ещё одной своей мечты — сжатых в DXT атласов, что решил "а почему бы и нет?" Провёл ещё некоторое время в исследованиях и экспериментах — и вуаля! Занимаемые текстурными атласами объемы памяти сократились вчетверо, а скорость отрисовки даже выросла на ~1-2ms!

После проведенной серии оптимизаций и рефакторингов результаты отрисовки следующие (для тестов я использую бюджетную видеокарту Radeon RX550):
~4.6 млн текстурированных полигонов при 60 fps,
~9.3 млн — при 30 fps.
Зависимость линейная. Сейчас объемы всех буферов не превышают 800 Мегабайт (но это с большим запасом) . Так что даже видеокарты с объемом видеопамяти в Гигабайт будут в деле 😉.

14K14K показов
1.3K1.3K открытий
47 комментариев

при нехватке видеопамяти, драйвер прозрачно начинает пользоваться оперативнойВообще, про эту фичу не то чтобы много кто в курсе. Помню ещё одному типу сказал, что видеокарта начинает заимствовать ОЗУ при нехватке своей памяти, так он меня долбоёбом назвал и даже слушать не стал. Хотя даже в диспетчере задач указан объём ОЗУ, который винда готова предоставить видеокарте, под названием "общая память графического процессора".

Но это конечно чисто костыль, чтоб приложение не крашилось при превышении объёма VRAM. Производительность сразу нахуй идёт.

Ответить

наоборот странно что кто-то этого не знал. Винда всегда при нехватке оперативки лезет в файл подкачки на диске, а видеокарты при нехватке своей памяти, в оперативку. Не дураки делали операционные системы. Всё заранее продумано и реализовано так, чтобы сократить вылеты по банальным причинам типа нехватки видеопамяти.
Я знал об использовании оперативки в качестве видеопамяти ещё в 2010-ом, когда начал смотреть тесты железа в играх. Там часто была статистика потребления видеопамяти и оперативки. И всегда было видно что чем меньше у видеокарты видеопамяти, чем больше потребление оперативки. Даже встроенная в процессоры графика всю жизнь использует оперативку под видеопамять. Раз встроенные в проц графические ядра могут использовать оперативку под свои нужды, то очевидно что и дискретные могут тоже самое. И не только диспетчер задач показывает виртуальную память видеокарты. Даже сам факт того, что игры, требующие кучу видеопамяти не вылетают за старых затычках, а просто выдают 5fps говорит о том, что видеокарта всегда найдёт что использовать в качестве памяти. Нехватки не будет никогда. Будет лишь снижение скорости передачи данных из-за использования заведомо более медленного устройства в качестве памяти. Во многих играх даже есть индикация того, сколько видеопамяти потребляют выбранные настройки качества. И при превышении объёма памяти видеокарты игры всё также запускаются и работают. Уже одного этого факта должно быть достаточно чтобы люди догадались что под видеопамять используется что-то ещё. И абсолютно очевидно что это оперативка, т.к. чаще всего она никогда не загружена на 100% и она вторая по скорости передачи данных в компе после видеопамяти.

Ответить

Хех, да я сам даже был в курсе, но понять, что вот это вот прямо сейчас происходит, не удалось - впервые с этим сталкиваюсь на практике. Зато теперь, когда симптомы известны, всюду мерещится переполнение видеопамяти :).

Ответить

Странно такие вещи отрицать, далбаеб видимо он сам кто отрицает, если врам Рип, то активно начинается все это лезь в рам, а если рам маловато то на хдд/ссд.

Краш очень быстро наступает если ещё откл файл подкачки при недостаточной памяти.

Ответить

Хотя даже в диспетчере задач указан объём ОЗУ, который винда готова предоставить видеокарте, под названием "общая память графического процессора".У меня 32 ГБ оперативной памяти и 16 ГБ видеопамяти. И винда готова выделить видеокарте еще 16 ГБ. Получается она может выделить максимум половину оперативной памяти?

Ответить

Очень интересный рассказ получился. А подскажи, у тебя движок с нуля написан или дарк енджин лежит в основе? Почему ты говоришь именно о том, что делаешь это под старые игры Thief, System Shock? Какого уровня ты хочешь добиться в итоге?

Ответить

Движок написан с нуля на C++. Он просто поддерживает форматы ресурсов Dark Engine: модели, миссии, текстуры. Сделать игру можно будет любого уровня графики (не, нанит, конечно, но это мы ещё посмотрим 🤣), планируется PBR, виртуальные текстуры и тени (я называю это стримингом).

Но для своих игр, я не планирую фотореалистичную графику, поскольку считаю, что она вредит погружению. Много раз об этом писал, если вкратце - из-за "зловещей долины", когда графика почти реальна, человек воспринимает её как реальность и требования у него к ней соответствующие, и как только он видит деталь или поведение, этой реальности не соответствующее, погружение рушится. Когда же графика стилизованная, то начинает работать мозг и достраивает в голове недостающие детали. Как результат - глубочайший уровень погружения, главное - держать всё в едином стиле, сбалансированно.

Ответить