Содержание статьи
Сразу признаюсь: в этой статье нет ни одной строчки кода, которую написал бы я. Я написал промпт. Пять строчек, с матом и с ошибками.
Всё остальное сделал 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 обрезал свет на единице, поэтому и выглядел таким плоским. Я об этом даже не знал.
Шаг 4. То, ради чего всё затевалось
Для DLSS 5 игра теперь сама отдаёт три буфера, и у каждого человеческое имя, которое видно в RenderDoc, Nsight и аддонах:
Буфер
Формат
Что внутри
SceneColor
RGBA16F
3D-сцена без HUD, меню и консоли
MotionVectors
RG16F
настоящие векторы: камера, двери, лифты, анимация монстров и ствол в руках
SceneDepth
D32
глубина в reverse-Z, без дальней плоскости
Плюс r_jitter (субпиксельный сдвиг камеры каждый кадр), r_scale (внутреннее разрешение, чтобы апскейлеру было что апскейлить) и r_debugview, который выводит векторы движения прямо на экран.
Векторы, кстати, он проверил глазами. При движении вперёд картинка правильно «разбегается» от центра, а у грунта с ружьём свои векторы, отдельно от стен.

Шаг 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. но об этом можно почитать в старых публикациях
