Почему выход новых игр сопровождается обновлением драйверов

Hardware | |

Инженер прошедший стажировку в Nvidia объясняет, почему выход новых игр сопровождается выходом соответствующих драйверов для видеокарт AMD и NVIDIA. Пост Promit (кстати, он является модератором) был опубликован на форуме GameDev.Net в ветке обсужения DX12/Vulkan/Mantle и раскрывает многие особенности драйверов видеокарт, которые могли ускользнуть из нашего вида. 

mantle hello triangle

Много лет назад мне довелось немного поработать в NVIDIA в команде разработки драйверов под DirectX (стажировался). Стояла эпоха Vista, когда многие были заняты переходом на DX10, переходом на новое железо и на новую ОС/архитектуру драйвера. В мои задачи входило брать игры, не работавшие на Vista, разбирать их на уровне драйвера и выяснять, почему они не работают. Я совершенно не считаю себя экспертом в области драйверов (да и работу свою делал плохо, если честно), но зато я многое узнал о том, как выглядят игры со стороны драйвера и ядра системы.

Первый выученный урок: проблемы возникают практически с каждой новой игрой. Речь об AAA-проектах от известных поставщиков. Иногда это грубые нарушения правил API – одна из D3D9-игр ни разу не вызывала BeginFrame/EndFrame. Иногда это ошибки или упущения – в одной были плохие шейдеры, сильно влияющие на производительность на драйверах NV. Ежедневно подобные случаи отправляются в баг-трекер. Дальше кто-нибудь разбирается, в чём дело, и готовит исправляющий патч для драйвера. Сейчас в драйвере есть множество опциональных патчей, включающихся для конкретных игр и производящих специфические для них изменения – вплоть до замены всех оригинальных шейдеров версиями от разработчиков драйвера. Никогда не задумывались, почему выход каждой популярной игры сопровождается выходом соответствующих драйверов от AMD и/или NVIDIA? Так-то.

Второй урок: драйверы огромны. Представьте 1-2 миллиона строчек кода для слоёв аппаратных абстракций, плюс ещё по миллиону для поддерживаемых API. Вспомогательная функция для Clear в D3D 9 вмещала тысячи строк одной лишь логики точного реагирования на команды. Затем она вызывала корректирующую функцию для непосредственного исправления нужного буфера. Уровень внутренней комплексности просто запредельный, даже в самом коде драйвера трудно понять, как добраться до поведений fast-path. Вдобавок, API неидеально работают с железом, так что даже в лучших случаях драйверу приходится заботиться о множестве вещей, которые вам неизвестны. Там внизу много, очень много теневых операций и теневых копий.

Третий урок: невозможно работать с потоками. Начиная примерно с 2005, независимые поставщики оборудования встраивали тонны многопоточности на внутреннем уровне драйвера. У них в доступе лучшие инженеры по работе с ядром/драйверами и буквально тысячи реальных полномасштабных тестов. Они выжали систему насухо, так что с сегодняшними API и драйверами невозможно получить существенную прибавку от многопоточности на стороне приложения. Если Futuremark способна лишь на 5-процентный прирост в обычном тесте, то у остальных нет ни единого шанса.

Четвёртый урок: поддержка нескольких GPU (SLI/CrossfireX) – охрененно сложная вещь. Вы даже представить себе не сможете необходимое количество неудачных попыток, пока не столкнётесь с этим сами. Думаю, работая с софтом, независимые поставщики оборудования тратят не меньше половины всех усилий на обеспечение корректной работы нескольких видеокарт в играх (как при этом выглядит работа с аппаратной частью, даже и не знаю). Если вы когда-нибудь пытались самостоятельно создать приложение с поддержкой нескольких GPU – особенно, если (да поможет вам бог) вы пытались сделать это в OpenGL – вы лично испытали на себе эту безумную кроличью нору. Есть лишь один быстрый путь, и он самый узкий из всех. Вспомните первые два урока, увеличив проблемы в сотни раз.

Глубокий вдох.

В итоге, новые API предназначены для решения всех четырёх проблем.

Почему игры плохо работают? Потому что API – штуки сложные, а валидация варьируется от пристойной (D3D 11) до плохой (D3D 9) и ужасной (OpenGL). Есть немало способов всё испортить, даже не замечая этого, и создатели драйверов зачастую предвидят ваши ошибки, динамически создавая патчи для распространённых случаев.

Осуществление поддержки драйверов при нынешней широте возникающих нестыковок – задача не из простых. AMD и NV могут найти для этого ресурсы, но поставщики помельче (Intel, PowerVR, Qualcomm, и т.п.) попросту не могут позволить себе необходимые вложения. Что важнее, стало совершенно невозможно объяснять разработчикам, как нужно делать конвейеры рендеринга. Слишком многое делается неправильно. Уже несколько лет как понятно, что нельзя продемонстрировать максимальную производительность без того, чтобы кто-то из NVIDIA или AMD взял исходный код вашей игры, запустил его на dev-драйвере и провёл практический анализ. Очень мало людей своими глазами видят исходный код игры, драйвер, на котором его запускают, ядро Windows, на котором его запускают, и знают полный список системных требований. Больше ни у кого нет такого уровня доступа или способностей.

Потоковость – просто катастрофа, и она переобдумывается с нуля. Чтобы её перенастроить или убрать, требуется обилие абстракции, потому что в старых версиях необходимо слишком сильное вмешательство драйвера для нормальной работы с потоками.

Работа нескольких видеокарт становится более прозрачной. Последние десять лет целью AMD и NV было сделать работу связок GPU понятными всем и каждому, и теперь ясно, что некоторым разработчикам это только усложнило работу. Драйверу нужно прибегать к несовершенной эвристике, чтобы понимать, что делает игра, а игра в свою очередь должна делать своеобразные вещи, чтобы среагировала правильная эвристика. Опять же, в больших играх кто-то садится и подгоняет одно к другому вручную.

Цель отчасти в том, чтобы просто перестать скрывать от разработчиков игр, что происходит внутри софта. Отладка драйверов никогда не была нам доступна, так что приходилось методом тыка экспериментировать и изучать, что именно замедляет конвейер рендеринга. Поставщики оборудования не спешили публично раскрывать эту информацию, которую они считают критической для удержания конкурентного преимущества (Конечно же, они так считают. Конечно, считают.). В итоге игра строит догадки, что там делает драйвер, драйвер предполагает, что делает игра, и всего этого хаоса можно было бы избежать, если бы драйверы были открыты.

Так почему же мы не сделали этого много лет назад? Всему виной политика и некоторые технические аспекты, но в конечном итоге всё сводится к высокой сложности написания кода под новые модели. Microsoft и ARB не хотели принуждать нас компилировать шейдеры под корректные состояния рендеринга, делать всё инвариантным, настраивать конфигурацию таблиц и куч и т.п. Нет ничего весёлого в поиске ошибок сегментации GPU. Их нельзя поймать в отладчике (пользовательском пространстве). В общем… подтекст, о котором многие не хотят говорить напрямую, в том, что новое поколение API создавалось в сотрудничестве с популярными движками. Mantle написан Йоханом Андерссоном (Johan Andersson) из DICE, а над Vulkan работали Арас П. (Aras P) из Unity, Никлас С. (Niklas S) из Epic и ещё парочка ребят из Valve.

Три из этих четырёх компаний недавно сделали свои движки общедоступными и бесплатными с минимальными финансовыми обязательствами.

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

[Обновлено]: Я не утверждаю, что API намеренно усложняются – труд инженеров сложен сам по себе. К тому же становится понятно, что DX11 и OpenGL будут поддерживаться в ближайшем будущем. Я убеждён, что это тоже повлияло на решение о выпуске новых систем.

Последний кусочек паззла: мы уже давно исчерпали аппаратные особенности, нацеленные на пользователя. Не считая чистой скорости, какие различия можно увидеть между GTX 480 и GTX 980 со стороны пользователя или разработчика? Было снято несколько ограничений (в особенности касательно вычислений), но разницы по сути нет. MS, сугубо из практических побуждений, пришла к выводу, что DX – это зрелая, стабильная технология, не требующая особой работы, и распустила большую часть соответствующей команды. Большинство ревизий GL были обычными исправлениями API (между прочим, GTX 480 может работать с полнофункциональным OpenGL 4.5). Так что причина, по которой мы вообще видим новые API, вытекает из того, что Андерссон доставал независимых поставщиков до тех пор, пока AMD не очнулась, почуяла возможность выгоды и начала проявлять интерес. Прошло, по существу, три года с момента, когда мы получили новое железо, до момента, когда мы можем внедрять вычисления напрямую в конвейер рендеринга, что сегодня считается нормой, а в 2012 казалось революцией. Важные люди вынудили нужных людей делать нужные вещи, и множество мелочей привели к кардинальным изменениям.

Фух. Даже не знаю, с чего я тут разглагольствую, но, надеюсь, это принесёт свои плоды. В конечном счёте, новые API – это правильный шаг, и они задним числом будут полезны для старого железа, что замечательно. Код под них будет писать сложнее. Насколько сложнее? Время покажет. Лично я считаю, что MS и ARB изначально пошли не в том направлении. Они хотели предоставить красивый front-end, а всю грязную работу делать тихо и незаметно. Да, код писать просто, но до чего же сложно его отлаживать или настраивать. Эту часть уравнения никто в расчёт не принимал. В итоге стало понятно, что сложность программирования под API – это нормально, если в итоге получается работающий результат. Мой опыт в перенастройке пока таков: это раздражает, требует обширного пересмотра кода, заставляет перепроверять кучу предположений и обычно требует масштабной инфраструктуры, прежде чем хоть что-то заработает. Но когда всё готово, сюрпризов больше не будет. Всё работает плавно и быстро, и если что-то работает медленно, это из-за вашего собственного кода, который можно проанализировать простыми средствами. Оно того стоит.

Поделиться

Обсудить