Культовый Quake 1996 года получил нативный движок на DirectX 12 с DLSS и без врапперов

Прокомментировать Просмотры: 0

Скриншот: E1M1 на RTX 5090, нативный DX12

Сразу сознаюсь: в этой публикации нет ни единой строчки кода, написанной мной лично. Я всего лишь сформулировал промпт — короткий, грубый и изобилующий опечатками.

Всю остальную работу проделал Claude, пока я отлучался на кухню: он написал 7 300 новых строк на C, C++ и HLSL, реализовал 64-битную версию, с нуля спроектировал рендерер под DirectX 12, а также добавил звук, поддержку мыши и даже успешно запустил тест на моём лэптопе. На всё про всё ушло полтора часа — с 21:28 до 22:55.

Зачем вообще возникла эта идея

Последний месяц я экспериментирую с DLSS 5 через ReShade и RenoDX практически во всех проектах, до которых удается дотянуться: от классического Pong и Lada Racing Club до Heretic и Nintendo Switch. Схема интеграции со старым софтом неизменно удручает. Игра выводит графику через устаревшие API вроде DX8–9, OpenGL или вообще силами процессора (как в DOS), поверх неё встает dgVoodoo2 для трансляции в DX11, затем прослойка-фидер, потом RenoDX и, наконец, нейросеть. Получается хрупкая пирамида из костылей, которая норовит обрушиться при первом же переключении по Alt-Tab.

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

И вот, сидя вечерком перед монитором, я разглядывал директорию с досовской демоверсией Quake (почему-то притаившуюся внутри папки с Heroes 2 — не спрашивайте) и подумал: а что, если вместо громоздких врапперов взять и кардинально переписать графический движок?

Само собой, собственных навыков для реализации такой задачи у меня нет.

Промпт

Приводить исходный текст не стану — цензура SE7ENа подобного не переживет.

Что именно было сделано (на понятном языке)

Процесс стартовал не с написания кода. Сперва нейросеть развернула Linux в облачной среде, установила mingw-w64 (кросс-компилятор для сборки виндовых исполняемых файлов под Linux), а также Wine и vkd3d для запуска DirectX 12 в безъядерном окружении. Работоспособность проверили на примитивном разноцветном прямоугольнике, который успешно отобразился на экране.

Затем с GitHub были загружены оригинальные исходники Quake, открытые id Software по лицензии GPL еще в 1999 году.

Шаг 1. Переход на 64 бита

Изначально Quake создавался под 32-битную архитектуру, что создало определенные трудности. Вся игровая логика (поведение монстров, анимация дверей и арсенал) написана на скриптовом языке QuakeC, где каждая строка адресуется как 4-байтовое смещение от начала таблицы. В 64-битной системе некоторые строки располагаются в памяти настолько далеко, что 4 байта под смещение уже не хватает. В результате игра либо аварийно завершалась, либо выводила абракадабру вместо наименований карт.

Claude создал отдельную таблицу для движковых строк с отрицательными индексами и переписал под нее около тридцати фрагментов кода.

Заодно вскрылись две давние ошибки разработчиков из id Software. Загрузчик уровней записывал восемь значений в массив, рассчитанный всего на четыре элемента, а аналогичный выход за границы памяти обнаружился в системе частиц светящихся объектов. Компилятор образца 1996 года такие вольности пропускал мимо ушей, однако современный GCC вполне способен превратить этот дефект в «оптимизацию», из-за которой уровень загрузится некорректно.

Шаг 2. Избавление от балласта

Устаревшие компоненты канули в Лету: OpenGL, DirectDraw, DirectSound, DirectInput, протокол IPX, поддержка модемов, воспроизведение музыки с компакт-дисков и ассемблерные оптимизации под процессоры Pentium. Итого минус 8 000 строк кода. Функционал игры от этого практически не пострадал — разве что возможность сражаться по нуль-модемному кабелю ушла в прошлое.

Шаг 3. Создание рендерера на DirectX 12 с нуля

На этом этапе технические подробности в отчете ИИ стали напоминать мне китайскую грамоту, поэтому перескажу суть в общих чертах:

• Вся геометрия уровня загружается в видеопамять единожды при старте. В дальнейшем процессор на базе стандартных BSP-деревьев определяет лишь видимые стены и передает графическому чипу массив индексов.

• Освещение поверхностей скомпоновано в единый текстурный атлас размером 1024×1024 пикселя (в четыре слоя), а мерцание факелов и ламп обрабатывается пиксельным шейдером без перезагрузки данных.

• Вспышки от выстрелов и взрывов ракет рассчитываются покадрово для каждого пикселя.

• Кадры анимации противников хранятся в видеопамяти, а шейдер плавно интерполирует соседние фазы, избавив наконец монстров от дерганой анимации с частотой 10 кадров в секунду.

• Эффекты неба и воды имитируются прямо в шейдере по оригинальным формулам софтверного Quake, избегая нарезки на полигоны, характерной для GLQuake.

Больше всего меня восхитила одна крошечная деталь. Нейросеть сверила уровень яркости с палитрой colormap из PAK0 и выставила коэффициент пересвета, идентичный досовскому оригиналу (до 1.94 от стандартного значения). В GLQuake этот параметр жестко обрезался единицей, из-за чего картинка казалась плоской. Сам я об этой тонкости даже не догадывался.

Шаг 4. Ради чего всё затевалось

Теперь движок самостоятельно генерирует три необходимых для DLSS 5 буфера с интуитивно понятными именами, которые отчетливо видны в RenderDoc, Nsight и профильных утилитах:

Буфер

Формат

Что внутри

SceneColor

RGBA16F

Трехмерная сцена без интерфейса, меню и консоли

MotionVectors

RG16F

Корректные векторы движения: перемещение камеры, створок дверей, лифтов, анимированных врагов и оружия в руках

SceneDepth

D32

Карта глубины в формате reverse-Z без дальней плоскости отсечения

Вдобавок были внедрены параметры r_jitter (субпиксельное смещение камеры на каждом кадре), r_scale (динамическое внутреннее разрешение для нужд апскейлера) и r_debugview для визуальной отладки векторов движения непосредственно на экране.

К слову, корректность векторов была проверена визуально: при движении вперед перспектива закономерно «расходится» от центра, а у пола и оружия просчитываются собственные смещения, независимые от стен.

Скриншот: r_debugview 1 и r_debugview 2
Скриншот: r_debugview 1 и r_debugview 2

Шаг 5. Финальные штрихи

Аудиоподсистема переведена на XAudio2, ввод с мыши обрабатывается через Raw Input, клавиатура реагирует на скан-коды (раскладка WASD функционирует даже при включенной русской раскладке), а геймпады задействуют API XInput. Окно жестко зафиксировано в разрешении 1600×1200 пикселей, поэтому Windows не искажает его при масштабировании интерфейса на 150%.

Подводные камни и непредвиденные трудности

Первый же запуск в облачной песочнице обернулся сбоем: встроенный компилятор шейдеров Wine спасовал перед синтаксисом HLSL. Переписывать код шейдеров ИИ не стал — вместо этого он извлек из дистрибутива Electron (легендарного движка Discord) оригинальную микрософтовскую библиотеку d3dcompiler_47.dll и продолжил тесты уже с ней. Логика безупречна: в моей системе Windows используется ровно этот же файл, но сам я до подобного трюка не додумался бы и за сто лет.

Затем ИИ принялся методично прогонять игру на программном рендерере (выдававшем от 5 до 40 кадров в секунду на двух ядрах), изучая заготовленные демки. В результате были обнаружены и устранены три забавных бага:

• При взгляде вниз оружие деформировалось в красную призму из-за перепутанного знака наклона модели.

• Игровое меню и консоль стали невидимыми, поскольку системный шрифт генерировался раньше загрузки палитры и оказывался абсолютно прозрачным.

• Окно запускалось на втором мониторе с разрешением 1600×1200, куда окно аналогичного размера с системной рамкой просто не помещалось. Теперь игра стабильно стартует на первичном дисплее.

Последний нюанс проявился уже на моем железе. Сообщив ассистенту, что рендеринг идет на встроенной графике, я получил логи, где черным по белому красовалось: Direct3D 12 on NVIDIA GeForce RTX 5090 Laptop GPU. Фокус в том, что мой второй монитор подключен к интегрированному чипсету, куда и транслировался готовый кадр. На всякий случай нейросеть прописала в исполняемый файл флаги NvOptimusEnablement и AmdPowerXpressRequestHighPerformance, чтобы мобильные драйверы безоговорочно перекладывали нагрузку на дискретную карту.

Автономное тестирование на моем компьютере

Этот эпизод заставил меня слегка поволноваться. Искусственный интеллект самостоятельно разместил скрипт в игровой папке, открыл файловый менеджер и дважды кликнул по исполняемому файлу. Игра проиграла деморолик (стабильные 60 FPS, ограниченные вертикальной синхронизацией), сделала пару скриншотов и закрылась, после чего ассистент проанализировал логи. Мое личное участие свелось к нулю — если не считать момента, когда меня попросили закрыть вручную запущенную мной ранее игру, так как файл был занят процессом.

Резюме

Итоговый исполняемый файл quake_dx12.exe весит около мегабайта, задействует исключительно базовые системные библиотеки Windows и напрямую взаимодействует с DirectX 12. Никаких прослоек вроде dgVoodoo и сторонних обёрток. Рядом уютно расположились файл pak0.pak из досовской демоверсии и исходный код (этого требует лицензия GPL).

Что сделал я? Нашел нужную папку, набросал корявый промпт с ошибками и один раз закрыл приложение.

Ну и прикрутил DLSS 5 — впрочем, об этом читайте в моих предыдущих заметках.

 

Источник

Похожие статьи

Поиск по играм, новостям и статьям…

Введите не менее двух символов

Введите не менее двух символов