Обращение к пользователям dlib, Dagon и других моих публичных проектов в связи с происходящими событиями

Я, Тимур Гафаров, создатель и мэйнтейнер dlib, Dagon, bindbc-wgpu, bindbc-newton, bindbc-soloud и др., в настоящее время живу в России без возможности в обозримом будущем покинуть эту страну.

В целях личной безопасности, не буду сейчас комментировать саму ситуацию и делать заявления, которые могут быть расценены как политические. Скажу только, что при работе над свободным ПО между людьми не должно быть границ, политика не должна мешать людям во всем мире делиться знаниями, создавать нечто новое, решать интеллектуальные задачи.

Тем не менее, реальность такова, что разработчики из России в любой момент могут оказаться отрезанными от мирового сообщества СПО. Это серьезно повлияет на проекты, в которых они принимают участие. Я искренне надеюсь, что этого не произойдет. В противном случае я, к моему огромному сожалению, не смогу управлять разработкой dlib, Dagon и других моих пакетов в реестре Dub. В отсутствие сопровождения эти пакеты будут постепенно терять актуальность. Также у меня пока нет уверенности, что я смогу получать финансирование через Patreon и PayPal.

До ухудшения ситуации, пока у меня есть доступ к GitHub и реестру пакетов, я продолжаю сопровождение dlib и Dagon, в настоящее время уделяя этому минимум своего свободного времени. Я на неопределенный срок приостанавливаю разработку dlib 2.0 и новых версий Dagon из-за негативного психологического фона и неясной картины будущего. В случае катастрофического развития событий я не обещаю, что смогу посвящать проектам даже минимальные время и силы.

В связи с этим, я с сегодняшнего дня не рекомендую использовать dlib, Dagon и другие мои пакеты в качестве критических зависимостей в важных программных продуктах. Кто хочет взять активную разработку на себя, создать форк — буду только рад. Пожалуйста, не делайте pull request’ы с большим количеством коммитов, существенными изменениями и новыми фичами, которые мне придется читать и проверять — у меня для этого может физически не оказаться возможности. Если у меня сохранится доступ к GitHub, я, скорее всего, увижу ваш форк и сам сделаю слияние, как только смогу.

P.S. Я в срочном порядке выпустил Dagon 0.13.0.

P.P.S. Облачная папка с примерами Dagon (Sponsor Folder), которая была ранее доступна только для подписчиков на Patreon, теперь открыта для всех.

dlib 1.0

Спустя 10 лет разработки моя библиотека общего назначения для геймдева наконец-то стабилизировалась — итог этой работы отражает первый мажорный релиз проекта, dlib 1.0.0.

Из наиболее важных нововведений могу отметить ускорение загрузки изображений (от 2 до 10 раз в зависимости от формата и веса файла) — оптимизация заключается в том, чтобы декодировать из буфера в памяти, а не напрямую из файлового потока. Добавлена валидация при создании POSIX-потоков, улучшен модуль dlib.math.interpolation.hermite — добавлена функция вычисления производной для сплайна Эрмита. Исправлено несколько важных багов в математическом и геометрическом пакетах.

В связи с этим знаковым релизом, а также тем, что в этом году dlib как публичному OpenSource-проекту исполняется ровно 10 лет, хочу немного рассказать об истории этой разработки.

dlib фактически начался как порт разнородных исходников с C++ на D. В 2010-11 годах, когда я познакомился с D, у меня был свой небольшой игровой движок на C++ (Phantom3D), а также библиотека общего назначения Sparx, и я решил портировать их на D2 — язык в те годы как раз стабилизировался. Начал, естественно, с векторной алгебры — так появился dlib.math, старейший и наиболее законченный из пакетов dlib. Сам 3D-движок, конечно, претерпел немало трансформаций и, в итоге, от старого кода почти ничего не осталось — мой следующий движок DGL был написан почти с нуля, хотя и начинался как полный порт Phantom3D и сначала носил то же название.

Sparx я поначалу использовал в D в виде динамически слинкованной библиотеки, через Derelict. Но это было не очень удобно, поэтому следующим этапом стало переписывание Sparx на D, в результате чего родился dlib.image — Sparx использовался в основном как загрузчик ресурсов (изображений PNG, JPEG, JPEG2000, TGA, BMP, DDS, моделей OBJ и MD5). Сейчас, правда, старого кода из Sparx в библиотеке очень мало — но, например, отдельные участки кода dlib.image, некоторые математические и геометрические функции напрямую унаследованы оттуда.

Сама идея создать такую библиотеку появилась в результате осмысления чужих открытых проектов, в особенности Давида Анри (чудо, что его сайт до сих пор существует!). Изучая чей-нибудь код, я часто замечал, что многие функции и классы можно сделать обобщенными и независимыми, что позволяет переиспользовать их в самых разных проектах — наверное, около половины кода любой игры можно вынести в библиотеку общего назначения. Это, в первую очередь, код, который не зависит от графического API и логики движка — линейная алгебра, вычислительная геометрия, работа с файловой системой, многопоточностью, взаимодействие с ОС, сетью и т.д. Главный принцип — скрыть платформозависимый код под абстрактным интерфейсом, так, чтобы приложению не приходилось взаимодействовать с операционной системой напрямую. Это делает код приложения на удивление простым.

Но вернемся к проектам Давида Анри. Меня очень впечатлили его загрузчики PNG, TGA и BMP — они очень сильно повлияли на мои собственные реализации декодеров этих форматов. Декодер JPEG — отдельная история, я написал его полностью самостоятельно, не заглядывая в готовые реализации — только читая спеки и техническую литературу (фактически, из заимствований в нем только ДКТ-преобразователь — хардкорная fixed-point математика, написанная темными магами). Модули для работы с векторами и матрицами — тоже отчасти влияние Анри, а именно его библиотеки Mathlib. Мои реализации, конечно, за 10 лет стали намного лучше — в dlib все алгебраические объекты обобщенные, одно и то же описание используется для векторов и квадратных матриц всех стандартных размерностей (2, 3 и 4). Выше 4 я поддерживать не стал, так как в компьютерной графике они практически не используются. Также у меня есть оптимизированные функции инвертирования и декомпозиции матриц, свизлинг векторов, огромное множество функций-фабрик и различных операторов — практически все, что может понадобиться для любых вычислений, связанных с трехмерными моделями.

Точная дата юбилея dlib — 28 сентября, так как именно в этот день я создал репозиторий на Google Code (я тогда еще использовал SVN, а не Git). Самый интересный этап в жизни проекта начался в 2013, когда разработка была перенесена на GitHub. Появился модуль dlib.core, к проекту примкнули новые разработчики: Martin Сejp провел огромную работу по реализации потоков ввода-вывода и абстрактной файловой системы (dlib.core.stream, dlib.filesystem), Eugene Wissner написал сетевой пакет (dlib.network) и аллокаторы памяти (dlib.memory), Nick Papanastasiou написал модуль комбинаторики, Вадим Лопатин (к слову, автор знаменитой читалки Cool Reader) помог улучшить декодер PNG, Роман Чистоходов и Андрей Пенечко внесли множество исправляющих патчей. Отдельное спасибо Роману за улучшенную поддержку BMP и TGA, а также ценные советы.

В 2015 году я начал рефакторинг, связанный с поддержкой ручного управления памятью. Эта грандиозная работа была полностью завершена в 2019 году. Динамическая память — это отдельная большая история, далеко выходящая за рамки D, я мог бы написать целую книгу на эту тему. Если теория вычислений разработана математиками и инженерами очень глубоко и досконально, то «теории памяти», в общем-то, до сих пор нет. Все, что мы имеем научного в этой сфере — это теория типов и ее высшее достижение, система типов Хиндли-Милнера. К сожалению, нет какого-то общего, формального подхода к управлению динамической памятью — сколько языков, столько и практик. Полагаю, что необходимо выделять парадигмы памяти, подобно тому, как существуют парадигмы программирования в целом.

В dlib я реализовал парадигму единственного владельца (single ownership), которая очень хорошо ложится на базовое ООП. Каждый объект может «владеть» другими объектами. У любого объекта бывает только один владелец, либо нет владельца вовсе (у корневых объектов). Когда удаляется владелец, удаляются и все объекты, которыми он владеет. Такой подход позволяет полностью избавиться от сборщика мусора и сделать высвобождение памяти детерминированным. Объектами в этом контексте может быть все, что угодно — парадигма не зависит от логики приложения — но, естественно, речь идет о данных, которые создаются один раз и надолго (в предельном случае — о неизменяемых наборах данных, которые создаются при старте приложения и удаляются при завершении работы). Эта модель не очень хорошо работает с алгоритмами, где нужно создавать и уничтожать объекты многократно, в цикле — но на то есть аллокация в стеке. Именно подход к работе с динамической памятью я считаю одним из главных достижений dlib — эту фичу, я думаю, можно и нужно нести в мир, который, кажется, сдался без боя сборщикам мусора.

Впрочем, рефакторинг, связанный со сменой парадигмы памяти, не привел к тому, что dlib стала @nogc-библиотекой (то есть, формально, по контракту гарантирующей отсутствие вызовов сборщика). Этой целью пришлось пожертвовать для сохранения обратной совместимости API. Но у меня есть план вернуться к этой проблеме в следующей версии, dlib 2.0 — расскажу о своих идеях в одном из будущих постов.

В 2016 году появился пакет dlib.audio, который может служить бэкендом для звуковых движков, плееров, DAW и т.д. Пока реализованы только базовые функции, но пакет будет развиваться. Также появился пакет dlib.network, который содержит независимую от Phobos поддержку сокетов и функции, связанные с вебом. В 2021 году работа над основной функциональностью библиотеки была завершена.

dlib — это, наверное, самый грандиозный мой проект за всю жизнь. Начавшись как маленький набор модулей «на все случаи жизни» для экспериментов с OpenGL, библиотека превратилась в один из самых популярных пакетов в реестре Dub. Сейчас количество загрузок dlib составляет 800-1000 ежемесячно. На основе dlib пишутся игровые движки, инструменты анализа изображений, GUI-тулкиты, даже синтезаторы и библиотеки генетических алгоритмов. О такой широкой области применения я, честно говоря, изначально даже не думал! К сожалению, D все еще остается довольно нишевым языком, и востребованность библиотек на нем ограничена востребованностью самого языка — но я счастлив уже тем, что сделал для сообщества что-то полезное. Накопление алгоритмов, достижений математической и инженерной мысли в одном месте, в удобочитаемом виде — я считаю, дело нужное. И, в конечном счете, это был интересный путь.

Как ускорить загрузку изображений

Совет пользователям dlib. Не декодируйте изображение напрямую из файлового потока, это слишком медленно. Вместо этого рекомендую загрузить файл в память целиком, создать поток массива (ArrayStream) и уже его передавать в функцию-декодер:

InputStream input = openForInput("image.jpg");
ubyte[] data = New!(ubyte[])(input.size);
input.fillArray(data);
ArrayStream arrStrm = New!ArrayStream(data);
SuperImage img = loadJPEG(arrStrm);
Delete(arrStrm);
Delete(data);
input.close();

Для JPEG, например, это дает ускорение в 5-10 раз, в зависимости от размера картинки.

Несколько новостей

  • Готовлю к выпуску dlib 1.0. Бета-релиз, скорее всего, состоится уже в январе — осталось довести покрытие до 50% (поставил такую цель несколько лет назад) и провести аудит некоторых модулей
  • Выложил все свои старые игры на этот сервер, ссылки в разделе Игры обновлены.

Новые статьи на Medium

Написал две новые статьи на английском:

  • dlib: Past, Present and Future — экскурс в историю dlib, о текущем статусе проекта и планах на будущее;
  • GitHub Actions and D — о том, как настроить тестирование на GitHub Actions для проектов на D.

Итоги 2020 года

Близится конец года, и это значит, что наступило время для традиционного подведения итогов по проектам.

  • У меня появился домен для личного бренда https://timurgafarov.ru, и, соответственно, блог переехал на новый адрес: https://gamedev.timurgafarov.ru.
  • Вышел Dagon 0.11.0. Движок был значительно улучшен, переработана структура модулей, практически полностью переписан рендер, исправлено множество проблем и узких мест производительности. Посмотреть движок в действии можно при помощи демки dagon-sandbox, а также на моем YouTube-канале. Также были дополнены уроки и примеры.
  • Вышли dlib 0.18, 0.19 и 0.20. У проекта появилась онлайн-документация, генерируемая из исходников при помощи Dub/ddox. В 2021 году dlib исполняется 10 лет!
  • Я опубликовал две новые статьи по D на Medium: Getting started with D и Const-correctness in D, а также небольшую вводную статью по WebGPU на CGWorld.
  • За этот год мне удалось собрать донатов на сумму $172,10. Огромное спасибо всем, кто перечислил деньги! Часть средств пошла на покупку аппаратного обеспечения — в частности, SSD (3590 руб.), наушников (790 руб.), USB-разветвителя (790 руб.), разветвителя для аудио (45,95 руб.). Также был приобретен графический софт: ArtRage (2423,38 руб.) и Armor Paint (1205,78 руб.). На оплату хостинга, на котором размещен этот блог, ушло 1447 руб. Кроме того, был куплен домен timurgafarov.ru за 199 руб. Итого израсходовано 10491,11 руб.

Ну и, конечно, не могу не назвать самые значимые для меня события в мире CG, СПО и геймдева:

  • Выход Blender 2.90 — очень впечатлила новая опция режима редактирования, позволяющая автоматически смещать UV-координаты синхронно с изменением геометрии. В целом Blender 2.80+ в моем восприятии превратился в полноценно рабочий инструмент, я начал использовать его в коммерческих проектах.
  • Выход бесплатной версии Unigine. Скачал, заценил — есть множество интересных фич, в частности понравился live reloading моделей и текстур при их обновлении внешними приложениями. Не понравилось, однако, то, что редактор нельзя запустить без входа в аккаунт (возможно, есть какой-то оффлайн-режим — не искал).
  • Открытие исходников NeoAxis. Пока детально не изучал этот движок, но в целом выглядит привлекательно.
  • Форк Dev-C++ от Embarcadero. До сих пор иногда пользуюсь этой IDE, поэтому новость для меня позитивная.

Документация по dlib

Наконец-то у dlib появилась онлайн-документация, генерируемая из исходников при помощи Dub: https://gecko0307.github.io/dlib/docs/dlib.html

Пока, разумеется, подробно задокументированы далеко не все модули, к большинству функций и классов есть только краткое описание — но я надеюсь, то, что есть, уже может кому-то пригодиться. Документирующие pull request’ы приветствуются (dlib использует синтаксис ddoc).

Временная шкала моих проектов

Приводя в порядок архивы и вспоминая прошлое, решил зафиксировать информацию о моих 3D-движках и других OpenSource-разработках в виде интерактивной временной шкалы при помощи time.graphics — возможно, кому-то будет интересно:

Итоги 2019 года

Пролетел еще один год, и это значит, что наступило время для традиционного подведения итогов по проектам.

  • Блог DLangGamedev переехал на новый адрес и движок: https://gamedev.timurgafarov.ru.
  • Был значительно улучшен движок Dagon: добавлена поддержка декалей, трубчатых источников света и «фонариков», кубических карт окружения. Благодаря сторонним разработчикам появилась поддержка рендеринга ландшафтов, в том числе процедурных с использованием шума OpenSimplex, а также интеграция GUI-тулкита Nuklear. Были полностью переписаны рендер и система постобработки в Dagon, упорядочена структура модулей движка, внесено множество оптимизаций производительности, реализован эффект объемного рассеяния света в атмосфере (volumetric light scattering) для направленных источников света. Mateusz Muszyński на основе Dagon и Nuklear написал клон Sokoban с редактором уровней.
  • Я начал работу по интеграции физического движка Newton Dynamics в Dagon в рамках проекта dagon-newton и биндинга bindbc-newton. Newton был выбран как наиболее функциональный физический движок с интерфейсом C.
  • Вышли dlib 0.16.0 и 0.17.0. Библиотека постепенно приближается к релизу версии 1.0.
  • Я написал bindbc-wgpu, биндинг к графической библиотеке wgpu.

Ну и, конечно, не могу не назвать самые значимые для меня события в мире CG, СПО и геймдева:

  • Появление WebGPU, нового веб-стандарта для высокопроизводительной графики.
  • Выход Blender 2.80 c новым вьюпорт-движком Eevee.
  • Открытие кода Mimalloc, быстрого и компактного аллокатора памяти от Microsoft.

Dagon 0.10.0 и dlib 0.16.0

Вышли новые версии движка Dagon и библиотеки dlib — 0.10.0 и 0.16.0 соответственно. Релиз Dagon один из самых крупных за всю историю проекта: он содержит 226 коммитов и труд четырех разработчиков (Тимур Гафаров, Mateusz Muszyński, Rafał Ziemniewski, dayllenger). Вот краткий список изменений:

  • Рендеринг ландшафтов, в том числе процедурных с использованием шума OpenSimplex.
  • Интеграция GUI-тулкита Nuklear.
  • Поддержка декалей для статических поверхностей.
  • Поддержка трубчатых источников света (tube area light) и «фонариков» (spot light).
  • Поддержка кубических карт.
  • Улучшенный HDR glow.
  • Множество новых функций для объектов Entity.
  • Поддержка твинов (tween) для анимации перемещения, поворота и масштаба объектов. Встроенный набор функций изинга включает linear, quad, back, bounce.
  • Улучшенный таймер.
  • Система конфигурации приложений.
  • Менеджер ввода (InputManager), позволяющий настраивать конфигурации клавиатуры и устройств ввода.
  • Поддержка отладочных сообщений от видеодрайвера (при помощи OpenGL-расширения GL_KHR_debug).

Полный список изменений смотрите на странице релиза. Также было обновлено и демонстрационное приложение.

Напоминаю: если вы заинтересованы в развитии этого проекта, то можете поддержать его на Patreon: https://www.patreon.com/gecko0307. Вы также можете сделать разовое пожертвование через PayPal: https://www.paypal.me/tgafarov. Заранее благодарен!