dlib 1.8.0

Очередное крупное обновление dlib. Главной фичей релиза является поддержка декодирования прогрессивных JPEG — жуткий долгострой, который я не мог довести до ума чуть ли не все последнее десятилетие. dlib.image.io.jpeg — это теперь пакет, где все части декодера разнесены в отдельные модули. Также теперь поддерживаются одноканальные JPEG.

Добавлен новый модуль dlib.random.ziggurat и функция gaussian, которая генерирует псевдослучайные числа с нормальным распределением по алгоритму Зиккурат.

Добавлена новая функция rsqrt в dlib.math.base. Это тот самый легендарный быстрый обратный корень Джона Кармака с оригинальными комментариями из кода Quake 3 — я решил добавить эту функцию в dlib как своего рода пасхалку.

Все математические константы в dlib.math.base приведены к единой точности (количеству знаков после запятой).

Исправлена ошибка в методе Array.removeKey.

dlib теперь использует Coveralls вместо Codecov для отслеживания покрытия кода.

Новый план по dlib2

В связи с выходом RTLD, я слегка изменил планы на вторую версию dlib. dlib2 будет по большей части независима от Phobos и основана на RTLD. Это будет по-прежнему библиотека для Windows и POSIX, но появится также поддержка AArch64. Большинство модулей будут вынесены в RTLD, сохраняя, насколько это возможно, обратную совместимость.

Что будет вынесено в RTLD без изменений в первую очередь:

  • Почти все из dlib.core
  • Контейнеры
  • Аллокаторы, арена
  • Базовая математика
  • Пул потоков (возможно, с некоторыми улучшениями)
  • Функции для работы с текстом

Разграничение функций между RTLD и dlib будет не по степени независимости от Phobos, а по степени фундаментальности. Не все функции dlib имеет смысл выносить в RTLD (вследствие спорного дизайна API или ограниченной области применения). Что, скорее всего, так и останется в dlib:

  • Линейная алгебра и вычислительная геометрия (нужны далеко не во всех приложениях)
  • Абстрактные потоки ввода-вывода (архитектурно тяжелые, нужны не всегда)
  • Интерфейс файловой системы (имеет специфический, не очень удобный API; в RTLD будет свой)
  • Работа с изображениями и звуком (специфическая область применения).

Новые фичи dlib2:

  • dlib2.image. Новый API для работы с изображениями, без SuperImage. Старый API будет реализован в пакете dlib поверх нового. Все декодеры графических форматов пойдут в dlib2, также появится декодер DDS. Основная задача пакета — максимальное упрощение прослойки, необходимой для хранения загруженных изображений в памяти и передачи в другие API, а также поддержка сжатых GPU-форматов. Будет использоваться структура, аналогичная TextureBuffer в Dagon. Этот же пакет будет включать функции-хелперы для создания текстур OpenGL ES.
  • Встроенная поддержка OpenGL ES в RTLD позволит создать новый движок холста для рисования векторной графики — dlib2.canvas. Будут реализованы растеризация сплайнов, функции плоттера, вывод изображений, простого текста и т.д. Это будет не полноценный графический движок, но, как минимум, инструмент для визуализации данных, полезный для научных исследований и прототипирования.

RTLD — низкоуровневый рантайм для D

Я начал разработку dlib в далеком 2011 году, когда писал на старом добром OpenGL 1.x под 32-битным Linux. Тогда я и не помышлял об альтернативных архитектурах и кросс-компиляции, мне даже в голову не приходило, что у D когда-нибудь появится поддержка такого количества разнообразных платформ. Но к началу 20-х уже стало очевидно, что грядет эпоха ARM, и старичку x86 придется потесниться, а сегодня AArch64 на устройствах мощнее телефона — уже не причуда и экзотика, а обыденная реальность. Стоит это принять и начать уже думать об ARM не как о мобильной, а как о второй десктопной архитектуре. Это необычное положение дел, потому что на десктопе x86 всегда царил монопольно. Но я вообще-то люблю изучать все новое, и для меня как графического разработчика освоение еще одной процессорной архитектуры — интересный вызов. Надо ли прямо сейчас всем кидаться портировать весь софт на ARM? Я думаю, нет, но какие-то базовые штуки, полезные библиотеки, фреймворки — желательно. Вот и я решил не оставаться в стороне.

Хорошая новость состоит в том, что D, в лице компилятора LDC, отлично поддерживает ARM. LDC — это неявный кросс-компилятор, он умеет генерировать код почти под все платформы, поддерживаемые LLVM, и это можно делать на любой хост-системе, не пересобирая тулчейн. ARM существует в разных аппаратно-программных вариантах, я под ним буду подразумевать aarch64-linux-gnu — то есть, платформу с процессором AArch64, ядром Linux и окружением GNU. Проще говоря, установив LDC, вы можете на x86 собирать приложения под десктоп или сервер на базе AArch64, о чем я уже писал в предыдущем посте.

Плохая новость состоит в том, что для компиляции под нестандартные таргеты нужны соответствующие версии Phobos и druntime. Я боюсь представить, что это за квест — собрать их, и не имею никакого понятия о статусе Phobos для AArch64. Мне это даже не очень интересно, потому что Phobos я считаю не самой удачной стандартной библиотекой, я ее почти не использую — она сильно перегружена, половину можно было бы оттуда вынести в дополнительные DUB-пакеты, многие модули откровенно устарели, некоторые функции работают странно, а шаблоны порой не компилируются. Для меня уже привычен хардкорный путь — игнорировать Phobos и писать все свое. К тому же, под маломощными одноплатниками с маленькой памятью вам все равно GC не друг, и лучше все делать на @nogc-компонентах.

Все эти измышления подвели меня к идее создания новой библиотеки общего назначения, более низкоуровневой, чем dlib. Я ее назвал RTLD — Real-Time Library for D. Она основана на модулях, которые я изначально писал для dlib2/dcore, а также на прямых портах из dlib 1.x. В отличие от dlib, это будет настоящая замена Phobos. RTLD предоставляет самую базовую функциональность, работая поверх libc, WinAPI, POSIX, системных вызовов Linux, а также динамически загружаемых функций — X11 и OpenGL ES. По сути, это библиотека для тех, кто хочет писать на D как на C, не используя ничего лишнего. Однако я намеренно не стал делать ее BetterC, в отличие от dcore. Взвесив все «за» и «против», я решил, что от BetterC на десктопе толку мало — от классов никуда не деться, они все равно потребуются на определенном уровне абстракции, поэтому зачем изначально себя их лишать?

https://github.com/gecko0307/rtld

На данный момент RTLD поддерживает Windows и Linux, компилируется под x86_64 и AArch64. Некоторые ее модули платформонезависимы, и их можно использовать в системной разработке, под bare metal.

Текущий список фич:

  • rtld.core — ключевые функции: работа с динамической памятью, загрузчик библиотек, потоки, мьютексы, создание окна
  • rtld.gl— биндинг к OpenGL ES 2/3 и создание контекста (поддерживаются Windows и Linux/X11)
  • rtld.libc — функции библиотеки C
  • rtld.math — элементарные математические функции
  • rtld.random — генератор псевдослучайных чисел
  • rtld.sys.windows — функции WinAPI
  • rtld.sys.posix — функции POSIX
  • rtld.sys.linux — функции Linux
  • rtld.text — текстовые кодировки
  • rtld.time — функции даты-времени, наносекундный таймер.

Закономерен теперь вопрос архитектуры будущей dlib2. Я склоняюсь к мысли отказаться от dcore в пользу RTLD — это будет базовый бэкенд dlib2, реализующий львиную долю функциональности, которая не зависит от GC. К сожалению, полный отказ от GC в dlib невозможен по ряду причин, но я надеюсь сильно сократить его присутствие.

dlib 1.7

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

Добавлен модуль dlib.math.base — замена std.math, которая заменяет некоторые часто используемые функции быстрыми интринсиками LLVM при компиляции LDC (подробнее об этом тут). Ускорены следующие элементарные функции: fabs, sqrt, sin, cos, tan, ceil, floor, round, trunc, rint, nearbyint, pow, exp, exp2, log, log2, log10, fmax, fmin, fma, copysign. В среднем они вдвое быстрее, чем в Phobos. Единственный нюанс, который следует помнить — лучше не импортировать оба модуля одновременно, иначе будет конфликт, и вам придется использовать квалифицированные имена при вызовах.

Добавлена встроенная SIMD-оптимизация для перемножения матриц 4×4 (Matrix4x4f). Это ускоряет оператор в 3-4 раза. В связи с этим удален модуль dlib.math.sse как устаревший, неподдерживаемый и, как выяснилось, нерабочий под x86_64.

Хэш-таблица FlatHashMap из Dagon 2.0 добавлена в библиотеку как dlib.container.hashmap. Реализация xxHash64, соответственно, — как dlib.hash.xxhash64 в новом пакете dlib.hash. Напомню, что это очень быстрая реализация ассоциативных массивов без использования сборщика мусора, которая в десятки раз эффективнее, чем dlib.container.dict и в 1,5-2 раза — чем встроенные ассоциативные массивы D.

Полностью переработан dlib.random — пакет теперь работает на основе перемешанного конгруэнтного генератора (PCG), реализованного для 32- и 64-битных чисел. Это современный эффективный генератор псевдослучайных чисел, который превосходит как Вихрь Мерсенна (std.random), так и линейный конгруэнтный генератор (rand в стандартной библиотеке C). Добавлены новые функции choice (случайный выбор из заданного множества вариантов) and rollDice (случайное целое число от 0 до заданного порога). Функция seed, генерирующая зерно для инициализации генератора из системной энтропии, теперь использует функцию смешивания fmix64 из MurmurHash3 вместо mix Боба Дженкинса.

Не используйте std.random!

Стандартная библиотека использует классический Вихрь Мерсенна (MT19937). Недавно я, вдохновившись эффективностью PCG для шейдерных вычислений, добавил соответствующий модуль в dlib, заменив им rand в dlib.random. Оказалось, что моя функция random для выборки равномерного распределения в диапазоне 0..1 аж в 4 раза быстрее, чем std.random.uniform!

Бенчмарк для 10000000 вызовов:

std.random.uniform: 77 ms, 38 μs, and 3 hnsecs total
dlib.random.random: 19 ms, 599 μs, and 3 hnsecs total

Стандартное отклонение у обеих функций примерно одинаковое:

std.random.uniform: 0.544σ
dlib.random.random: 0.413σ

Не используйте ассоциативные массивы!

Шучу, конечно. Но специализированная хэш-таблица действительно может быть заметно быстрее встроенных ассоциативных массивов D. На днях начал писать менеджер ассетов для Dagon 2.0 и решил заодно заменить dlib.container.dict для хранения ссылок на ассеты чем-то поэффективнее. Результатом стал класс FlatHashMap — таблица, которая хранит все элементы в одном непрерывном блоке памяти, что значительно улучшает производительность за счет кэш-дружественности. Кроме того, такая таблица работает в среднем за постоянное время, что выгоднейшим образом отличает ее от префиксного дерева (на котором основан dlib.container.dict) — скорость префиксного дерева пропорциональна длине ключей, а flat hash map работает с хэшами фиксированного размера.

В качестве хэш-функции я взял быстрый xxHash64, который можно использовать в реальном времени. Моя версия в Dagon — это частичный D-порт xxhash-clean, минимальной референсной реализации xxHash. Результаты бенчмарка следующие: вставки у FlatHashMap получились аж в 36 раз быстрее, чем у dlib.container.dict, и в 1,7 раз быстрее, чем у ассоциативных массивов. Поиск — в 15 раз быстрее, чем у dlib.container.dict, и в 1,6 раз быстрее, чем у ассоциативных массивов. Эффективен и opApply — это обычный линейный обход буфера безо всякой рекурсии и сложной логики.

Обновления

Dagon 1.5.0

Добавлены расширения dagon:network и dagon:security, реализующие, соответственно, сетевой интерфейс на базе UDP/ENet и безопасный транспортный протокол.
Функции для работы с кубическими картами из dagon.graphics.texproc вынесены в отдельный модуль dagon.graphics.cubemap.
Метод Application.takeScreenshot теперь не использует сборщик мусора.

dlib 1.6.0

Добавлен новый пакет dlib.time с реализацией кроссплатформенного интерфейса к системным функциям времени-даты. Временные метки (DateTime) имеют точность до целых секунд и предназначены для относительно редких событий (логирование, онлайн-переписка, календарные события и т.д.). Для более точного измерения времени следует использовать наносекундный счетчик в std.datetime.stopwatch, либо специализированные таймеры внешних библиотек (например, таймер SDL).
Добавлен новый модуль dlib.container.sorting с реализациями алгоритмов сортировки выбором и вставками. Они лучше всего подходят для сортировки небольших массивов нечасто обновляющихся данных в реальном времени. Например, если массив уже отсортирован, то функция сортировки вставками insertionSort будет в сто раз (!) быстрее, чем std.algorithm.sort, что идеально для игр.
Методы встроенного мьютекса Mutex помечены как @nogc.

Обновления

Dagon 1.4.0

Добавлена поддержка сжатия текстур в BC4 и BC7, бэкпортированная из Dagon 2. Компрессор BC7 является D-портом библиотеки bc7enc Рича Гелдриха, компрессор BC4 написан с нуля.
Модуль dagon.resource.sdlimage объединен с dagon.resource.texture.
prefilterCubemap теперь принимает опциональный параметр lumaScale, коэффициент яркости для входных значений кубмапа.
Исправлен баг, из-за которого свойство Application.fullscreen не обновлялось по конфигу.

dlib 1.5.1

Небольшой багфикс-релиз dlib 1.5, в котором был исправлен неприятнейший баг декодера JSON — пустые объекты и массивы интерпретировались как узлы JSONType.Null.

Обновления

Dagon 1.3.0

Выпустил новую версию движка, до этого также вышло несколько багфиков для 1.2. Метод Application.takeScreenshot теперь совершает полную отрисовку кадра, чтобы все изменения в рендере отразились на скриншоте. Добавлены методы Material.copyTo и Material.copyFrom для копирования свойств одного материала в другой. Добавлено свойство Entity.blurOnlyRotation, предназначенное для ограничения motion-векторов угловой скоростью, игнорируя изменение позиции — это важно для объектов, которые вращаются, но не меняют своего положения в экранном пространстве, таких, как колеса машины.

Улучшен префильтр яркости для эффекта glow: добавлен верхний порог яркости, а нижний теперь не срезает пиксели жестко, используя технику soft-knee для плавной градации. Разница заметна, например, при свечении неба, которое имеет неравномерную яркость. Соответственно, добавлены новые опции render.conf: glow.softKnee, glow.maxEnergy.

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

dlib 1.5.0

Вышло небольшое обновление dlib. Добавлена функция trsMatrix для создания матрицы трансформации из смещения/поворота/масштаба за один шаг, без перемножений. Появилась функция lerpAngle для линейной интерполяции углов по кратчайшей дуге, а также shortestAngleDelta для вычисления угловой дельты по кратчайшей дуге. Добавлена новая изинг-функция easeOutElastic. Функция wrapAngle теперь работает с любыми угловыми единицами при помощи опционального параметра периода (по умолчанию период равен 360, что означает градусы).

Chillwave Drive

Игра была значительно обновлена: добавлен подсчет кругов и гоночная логика, главное меню, экран загрузки, уникальная покраска кузова для каждого участника заезда, поддержка различных материалов покрытия, влияющих на физику, интерфейс в режиме паузы, поддержка вибрации контроллера, поддержка суперсэмплинга при снятии скриншотов и многое другое. Симулятор приближается к полнофункциональной альфа-версии.

Обновления

Продолжаю улучшать объемное рассеивание света, появившееся в Dagon 0.34.0 — на днях добавил его поддержку для конусных источников света. Теперь можно создавать эффекты прожекторов и фонарей.

Обновления библиотек:

  • dlib 1.4.1 — небольшое обновление dlib, в котором исправлен сеттер OBB.center;
  • bindbc-wgpu 0.27.0 — синхронизация с wgpu-native 27.