Когда дедлайны горят, а легаси-база данных снова выдает таймауты, хочется сделать с ней что-то радикальное — например, запустить в ней культовый шутер 1993 года. Звучит как бред митапа после полуночи, но для инженеров, привыкших выжимать из СУБД невозможное (и молиться, чтобы на проде ничего не упало), «It runs Doom» — это не шутка, а полноценный вызов. Сегодня мы разберем этот безумный инжиниринг: как заставить декларативный язык таблиц обсчитывать физику, рендерить графику и крутить кадры без единой строки C++.
Введение: когда реляционная алгебра встречается с демонами ада
Мировое IT-сообщество давно одержимо культовой фразой: «It runs Doom». За прошедшие десятилетия классический шутер 1993 года запускали на самых неожиданных устройствах и средах: от осциллографов и банковских терминалов до тестов на беременность и клеток ДНК. Инженеры соревнуются в изобретательности, заставляя рендерить трёхмерный мир на оборудовании, которое для этого абсолютно не предназначено.
Однако один из самых безумных и технически сложных экспериментов в этой области — это попытка портировать Doom внутрь реляционной системы управления базами данных (СУБД), используя исключительно SQL и процедурные расширения вроде PL/pgSQL. На первый взгляд, эта идея звучит как чистейшая ересь. SQL — это декларативный язык для работы с таблицами, оптимизированный под хранение, выборку и агрегацию данных. Doom — это процедурная, событийно-ориентированная программа на C, требующая жесткого контроля над памятью и стабильных 35 кадров в секунду (что звучит как несбыточная мечта для любого джуниора после первого релиза). Как можно заставить реляционный движок обрабатывать трассировку лучей, физику коллизий и обработку ввода?
В этой статье мы подробно разберем, как энтузиасты смогли реализовать оригинальный Doom внутри базы данных, с какими архитектурными вызовами они столкнулись, как обошли ограничения SQL и какова цена такого подвига с точки зрения производительности.
Архитектурный вызов: как превратить реляционные таблицы в игровой движок
Главное противоречие между Doom и SQL заключается в самой парадигме вычислений. В классическом программировании состояние игры хранится в оперативной памяти в виде структур (struct), указателей и массивов. Процессор последовательно обходит эти структуры в каждом игровом цикле (game tick).
В мире SQL все данные должны быть нормализованы и разложены по таблицам. Чтобы заставить СУБД выполнять функции игрового движка, разработчикам пришлось совершить ментальный сдвиг и представить каждый элемент игрового мира как строку в реляционной таблице:
- Стены и сектóры (WAD-данные): Геометрия уровня конвертируется в реляционные структуры данных. Вершины, линии и подсектора хранятся в связных таблицах с индексами для быстрого пространственного поиска.
- Сущности (Mobj): Враги, патроны, пули и сам игрок представлены в единой таблице объектов с координатами $(x, y, z)$, углами обзора, векторами скорости и состоянием здоровья.
- Кадровый буфер (Framebuffer): Экран игры размером 320x200 пикселей предстает в виде огромной матрицы или таблицы пикселей, где каждый пиксель обновляется с помощью SQL-запросов (прямо как те самые «оптимизированные» дашборды в Excel на клиентских ноутбуках).
Основной цикл игры (Main Loop), который в оригинале выглядит как бесконечный цикл на C, внутри СУБД реализуется через хранимые процедуры, рекурсивные CTE (Common Table Expressions) или триггеры.
Рендеринг графики и обработка физики через реляционные запросы
Самая сложная задача при портировании Doom на SQL — заставить реляционный движок заниматься отрисовкой графики (Raycasting) и обсчетом коллизий в реальном времени. В реляционных базах данных нет понятия «экрана» или «видеопамяти» в привычном понимании GPU, поэтому всю математику проекции приходится выполнять силами SQL-запросов.
Трассировка лучей и BSP-дерево
Оригинальный Doom использует BSP-дерево (Binary Space Partitioning) для быстрого определения видимости стен от точки зрения игрока. В СУБД это дерево преобразуется