Quake 1996 года на нативном DirectX 12. Без врапперов, без dgVoodoo и без меня. но с ДЛСС 5 - QubStore

Quake 1996 года на нативном DirectX 12. Без врапперов, без dgVoodoo и без меня. но с ДЛСС 5

Содержание статьи

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

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

Зачем вообще это понадобилось

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

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

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

Сам я этого, понятно, не умею.

Промпт

Целиком приводить не буду, Хабр такие слова не одобрит.

Что он сделал. В переводе на человеческий

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

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

Шаг 1. 64 бита

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

Claude завёл отдельную таблицу «движковых» строк с отрицательными номерами и переписал под неё около 30 мест в коде.

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

Шаг 2. Выкинуть старьё

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

Шаг 3. Рендер на DirectX 12 с нуля

На этом месте его отчёта я честно перестал понимать слова уже к третьему абзацу, поэтому коротко и как я это понял:

• Вся геометрия карты один раз при загрузке заливается в видеопамять. Дальше каждый кадр процессор по родному BSP только решает, какие стены видно, и отдаёт видеокарте список номеров.

• Свет на стенах упакован в атлас 1024×1024 из 4 слоёв, а мигание факелов и ламп считает шейдер, ничего не перезаливается.

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

• Все кадры анимации монстров лежат в видеопамяти, и шейдер плавно смешивает соседние, так что огры наконец перестали дёргаться 10 раз в секунду.

• Небо и вода «плывут» прямо в шейдере по формулам софтверного Quake, без нарезки на полигоны, как это было в GLQuake.

Больше всего меня порадовала одна деталь. Яркость он сверил с таблицей цветов colormap из PAK0 и выставил пересвет как в DOS-версии, до 1.94 от нормы. GLQuake обрезал свет на единице, поэтому и выглядел таким плоским. Я об этом даже не знал.

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

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

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

Буфер

Формат

Что внутри

SceneColor

RGBA16F

3D-сцена без HUD, меню и консоли

MotionVectors

RG16F

настоящие векторы: камера, двери, лифты, анимация монстров и ствол в руках

SceneDepth

D32

глубина в reverse-Z, без дальней плоскости

 

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

Векторы, кстати, он проверил глазами. При движении вперёд картинка правильно «разбегается» от центра, а у грунта с ружьём свои векторы, отдельно от стен.

Quake 1996 года на нативном DirectX 12. Без врапперов, без dgVoodoo и без меня. но с ДЛСС 5

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

Шаг 5. Всё остальное

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

Подводные камни. Их было больше, чем у меня со Свитчем

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

Потом он часами… ну ладно, минутами гонял игру на программном рендере (от 5 до 40 FPS на двух ядрах) и разглядывал скриншоты демок. И нашёл у себя три бага:

• Ствол при взгляде вниз превращался в красную призму, потому что у моделей был перевёрнут знак наклона.

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

• Окно открылось на втором мониторе, а он у меня сам 1600×1200, и окно 1600×1200 с заголовком туда физически не влезает. Теперь игра открывается на основном экране.

И последний, уже на моём железе. Я написал ему, что игра рендерится на встройке. Он полез в лог, а там чёрным по белому Direct3D 12 on NVIDIA GeForce RTX 5090 Laptop GPU… Просто второй монитор у меня подключён к встроенной графике, и готовый кадр копируется туда. На всякий случай он вшил в экзешник флаги NvOptimusEnablement и AmdPowerXpressRequestHighPerformance, по которым драйвер ноутбука сразу отдаёт игру дискретке.

Как он тестировал на моём компе

Вот тут я немного напрягся. Он сам положил в папку игры скрипт, сам открыл Проводник и сам дважды кликнул по exe. Игра прогнала демку (60 FPS, упёрлась в VSync), сделала два скриншота и вышла, а он прочитал лог. Меня в этой цепочке не было вообще. Ну, кроме момента, когда он попросил закрыть игру, потому что я её запустил сам и exe был занят.

Что в итоге

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

Сделал ли я что-то сам? Нашёл папку, написал промпт с ошибками и один раз закрыл игру.

Ну и прикрутил длсс 5. но об этом можно почитать в старых публикациях

Ссылка на основную публикацию