Назад к портфолио
Приложения

Blitz3D: Re

Модернизация компилятора и runtime Blitz3D для современных версий Windows. Идея появилась во время работы над русификацией Hard Time: вместо того чтобы постоянно обходить ограничения старого компилятора и DirectX 7, я решил доработать сам инструмент. Основные цели проекта: нормальная работа на Windows 10/11, поддержка x64, сохранение совместимости со старыми .bb проектами, современный графический backend, исправление проблем с производительностью и дальнейшая работа с Unicode и кириллицей.

В работе75%
Blitz3D: Re

Модернизация Blitz3D для современных Windows

Идея заняться Blitz3D появилась во время моей работы над русификацией Hard Time. Игра написана на оригинальном движке Blitz3D, поэтому довольно быстро я столкнулся не только с ограничениями самой игры, но и с возрастом инструментария. Оригинальный Blitz3D и большая часть его форков рассчитаны на совсем другую эпоху Windows: старый DirectX 7, 32-битная архитектура, проблемы с современными драйверами, странное поведение на Windows 10 и 11, сложности с кириллицей и устаревшие зависимости.

В какой-то момент я решил не ограничиваться исправлениями конкретно для Hard Time, а попробовать привести сам компилятор и runtime в более современное состояние. Основная идея проекта при этом довольно простая: старый Blitz3D код должен продолжать работать без необходимости переписывать игру под новый движок. В будущем я хочу объединить эту работу со своим русификатором Hard Time и получить более современную сборку игры, сохранив при этом оригинальный движок и его поведение.

Основные цели

Главными задачами стали нормальная сборка программ под x86 и x64, совместимость с Windows 10 и Windows 11, сохранение старого синтаксиса Blitz3D и возможность собирать существующие .bb проекты без массовых изменений исходного кода. Отдельное внимание я уделяю графике и производительности, чтобы старые проекты не требовали обязательного использования dgVoodoo или режима совместимости.

Также в план входит улучшение поддержки кириллицы и Unicode, корректная работа старых DLL и пользовательских библиотек, а также сохранение поведения оригинального Blitz3D там, где оно важно для существующих игр.

Переход на x64

Одной из первых больших задач стала полноценная 64-битная сборка. В старом коде оказалось довольно много мест, где указатели, адреса и числа неявно считались 32-битными. Некоторые части формально могли собираться под x64, но обрезали адреса, неправильно передавали данные в DLL или ломались только при определенных значениях.

В ходе работы были доработаны CallDLL, пользовательские библиотеки, работа с указателями, преобразование строк и 64-битных чисел, integer constants в компиляторе, генерация standalone EXE и LLVM JIT. При этом я стараюсь не менять старое поведение x86 без необходимости, поскольку совместимость с уже существующими проектами для меня важнее формального приведения всего к современным типам данных.

Проблемы Direct3D 7

Отдельной большой темой стала графика. Blitz3D использует DirectDraw и Direct3D 7, которые на современных версиях Windows существуют в основном благодаря слою обратной совместимости. На x86 этот backend все еще работает и во многих случаях правильно отображает старые игры, но производительность отдельных операций оказалась очень плохой.

Например, обычный интерфейс с большим количеством изображений с цветовой маской мог работать настолько медленно, что даже перемещение по меню становилось затруднительным. После профилирования выяснилось, что основная задержка в таком случае возникает не в логике игры и не в выводе готового кадра, а непосредственно в старых masked blit операциях DirectDraw.

На x64 ситуация еще сложнее. На тестовой системе DirectDraw interface создается, но Windows не предоставляет IDirect3D7, поэтому использовать старый D3D7 backend как полноценный современный x64 renderer уже невозможно. При этом он остается очень полезным как эталон, с которым можно сравнивать внешний вид графики и поведение старых команд.

OpenGL backend

Интересно, что OpenGL backend уже присутствовал в исходном проекте. Его не пришлось писать полностью с нуля, но он находился в довольно сыром состоянии и заметно отличался по поведению от классического Blitz3D.

Я решил постепенно довести его до совместимости. По мере тестирования обнаружилось довольно много старых ошибок: цветовая маска изображений отображалась ярко-пурпурным цветом, некоторые текстуры 3D моделей становились полностью черными, неправильно работали отдельные texture slots, отличалось поведение alpha у спрайтов и теней. Также нашлись проблемы с framebuffer и viewport, из-за которых после работы с изображениями могла пропасть 3D сцена или уменьшиться интерфейс.

Отдельной проблемой оказался ResizeImage, который в некоторых случаях мог занимать несколько секунд из-за неправильной работы вложенных блокировок изображения. Такие ошибки я стараюсь сначала воспроизводить на маленьком отдельном примере, а уже потом исправлять runtime. Это позволяет не писать специальные костыли под конкретную игру, а исправлять именно поведение Blitz3D.

Производительность OpenGL

После исправления основных визуальных проблем стало заметно, что OpenGL все еще проигрывает старому D3D7 в сценах с большим количеством объектов. Я начал отдельно измерять время UpdateWorld, RenderWorld и вывода кадра, чтобы понять, где именно теряется производительность.

Оказалось, что значительная часть проблемы была связана не с самим OpenGL, а с реализацией backend. Runtime многократно очищал texture units для каждого материала, выполняя большое количество лишних вызовов драйвера. После исправления этой части время рендера заметно снизилось.

Позже нашлась еще более серьезная причина. В Release-сборке после практически каждого OpenGL вызова выполнялся glGetError, то есть готовая версия программы фактически работала с тяжелой отладочной проверкой каждой команды. После отключения этой проверки в Release производительность выросла в несколько раз.

На отдельном тесте с 15 анимированными персонажами x64 OpenGL в результате приблизился по времени рендера к старому D3D7 x86. При этом OpenGL не страдает от крайне медленных DirectDraw операций, которые хорошо заметны в 2D интерфейсах на современных Windows.

Проверка на Hard Time

Hard Time остается для меня хорошим практическим примером, поскольку именно с этой игры началась вся работа. При этом я стараюсь не писать исправления специально под нее. Игра используется скорее как большой реальный проект, на котором проявляются проблемы, которые трудно заметить в небольших тестах.

На текущем этапе Hard Time уже удается собрать как x64 приложение с OpenGL backend. Работают меню, 3D сцены, анимация персонажей, текстуры, интерфейс и большая часть проверенных графических эффектов. Ряд проблем, которые изначально были заметны именно в игре, позже удалось воспроизвести отдельно и исправить уже на уровне runtime.

В результате постепенно получается та связка, ради которой проект и начинался: мой русификатор остается работой над самой Hard Time, а доработанный Blitz3D может стать основой для более современной версии игры, рассчитанной на актуальные системы.

Тестирование

По мере работы я значительно расширил количество автоматических проверок. Изменения тестируются отдельно для x86 Debug, x86 Release, x64 Debug и x64 Release, а для найденных ошибок я по возможности сохраняю минимальный пример, который воспроизводит проблему.

Так появились отдельные тесты для DLL, integer ABI, графических масок, texture slots, animated meshes, alpha, framebuffer state, viewport, изображений и производительности. Для такого проекта это особенно важно, потому что задача состоит не только в том, чтобы новый код работал, но и в том, чтобы случайно не сломать старые программы.

Что осталось

Работа пока не закончена. Еще нужно проверить и довести переключение графических режимов, fullscreen, Alt+Tab, minimize/restore, DPI, восстановление ресурсов и поведение на разных конфигурациях Windows и видеокарт. Отдельный большой этап связан с Unicode, кириллицей и файловыми путями.

Также предстоит подготовить нормальную схему распространения готового x86/x64 компилятора и решить, какой графический backend должен использоваться по умолчанию. Но основная архитектурная часть уже работает: старый Blitz3D код можно собирать под x64, OpenGL backend запускает реальные проекты, а многие ограничения старого DirectX 7 пути удалось обойти без изменения исходного кода самих игр.

Для меня это не попытка сделать новый игровой движок вместо Blitz3D. Скорее наоборот, я хочу сохранить старый Blitz3D настолько, насколько это возможно, но сделать его снова пригодным для использования на современных компьютерах.