Alcinoe — это библиотека компонентов с открытым исходным кодом для Delphi и C++Builder, которую поддерживает на GitHub разработчик Zeus64. Она покрывает области, которые VCL и RTL FireMonkey оставляют сторонним разработчикам: видеоплеер с GPU-ускорением, обёртку для WebRTC, нативные элементы редактирования для iOS и Android, парсер JSON/BSON с двумя режимами, клиент MongoDB с пулом соединений, обёртку для ImageMagick, а также набор элементов FireMonkey, полностью обходящих штатный конвейер отрисовки. Библиотека завоевала репутацию на версиях Rio (10.3.3) и Sydney (10.4.2) и с тех пор отслеживает каждый релиз Embarcadero. На момент написания она полностью совместима с Delphi 11.1 Alexandria и Delphi Athens 12.3
Как добавить Alcinoe в проект
Установка разветвляется на одном вопросе: нужна ли вам поддержка на этапе проектирования для визуальных элементов Alcinoe? Если нет, пропустите BPL целиком. Добавьте {alcinoe_rootdir}\source в путь поиска библиотек проекта — и готово. Каждый невизуальный компонент, включая парсеры, клиенты баз данных и строковые утилиты, компилируется из исходного кода без какой-либо регистрации
Когда поддержка на этапе проектирования всё же нужна, путь немного длиннее. Откройте Component > Install Packages в IDE Delphi, найдите BPL, соответствующий вашей версии (например, {alcinoe_rootdir}\lib\bpl\alcinoe\Win32\alexandria\Alcinoe_alexandria.bpl), установите его, и всё равно добавьте {alcinoe_rootdir}\source в путь поиска. BPL регистрирует компоненты; каталог исходного кода — это то, что находит компилятор при сборке вашего проекта
Alcinoe поставляет опциональные патчи к исходникам RTL Embarcadero. Если они вам нужны, перейдите в {alcinoe_rootdir}\embarcadero\, выберите подкаталог для вашей версии и запустите update.bat. Сценарий рассчитывает на наличие GIT в PATH и предполагает стандартное расположение установки Embarcadero. Он загружает оригинальные исходники RTL и применяет патчи. После этого добавьте каталог с пропатченными исходниками в путь поиска вашего проекта, чтобы компилятор нашёл их раньше, чем копию только для чтения в дереве установки Embarcadero. Ничего из этого не требуется для начала работы; это важно, только если вы столкнётесь с ошибками, которые устраняют патчи
Android и прокси десугаринга D8
Несколько компонентов Alcinoe (WebRTC, видео на базе ExoPlayer) зависят от библиотек Java, использующих языковые возможности Java 8. Инструментарий Android, поставляемый со старыми версиями Delphi, использует dx.bat для преобразования в DEX, который не может обработать эти байт-коды на уровнях API ниже 26. Решение — десугаринг, который D8 выполняет автоматически при прямом вызове. Alcinoe предоставляет прокси-сценарий по пути {alcinoe_rootdir}\tools\D8Proxy\dx.bat, который перенаправляет вызовы из системы сборки Delphi в D8, делая десугаринг прозрачным. Замените оригинальный dx.bat в каталоге build-tools вашего Android SDK (обычно C:\SDKs\android\build-tools\30.0.3\) этим прокси. Embarcadero отслеживала исходную проблему под номером RSP-24155; более поздние версии инструментов SDK устранили её напрямую, поэтому проверьте, требуется ли вашему текущему инструментарию этот обходной путь
Проблема отрисовки FireMonkey и ответ Alcinoe
Стандартный цикл отрисовки FireMonkey становится узким местом в интерфейсах с интенсивной прокруткой. Один TRectangle со скруглёнными углами может перерисовываться около 3 мс, потому что штатная реализация пересчитывает контур на каждом кадре. При 20 таких видимых элементах это складывается в 60 мс на проход кадра, что ограничивает эффективную частоту кадров значительно ниже порога плавной прокрутки
Alcinoe решает это с помощью GPU-резидентного буфера на каждый элемент. Первая отрисовка рендерит элемент в TTexture, хранящийся в памяти GPU. Последующие перерисовки выполняют блиттинг этой текстуры вместо повторного выполнения алгоритма отрисовки. Измеренный результат на том же скруглённом прямоугольнике падает примерно с 3 мс до примерно 0,1 мс. Помимо буферизации, Alcinoe заменяет отрисовку контуров OpenGL для базовых фигур нативными API рисования Android и iOS, обходя компромисс качество/производительность, привязанный к Form.Quality. Соответствующие элементы — это TALRectangle, TALCircle и набор улучшенных контейнеров компоновки, включая ScrollBox и TabControl
TALJsonDocument: DOM и SAX в одном типе
TALJsonDocument — это парсер JSON и BSON от Alcinoe. Он поддерживает два режима обхода. Режим DOM строит дерево объектов в памяти, обеспечивая произвольный доступ к любому узлу ценой памяти, пропорциональной размеру документа. Режим SAX генерирует события по мере чтения парсером каждого токена, не удерживая никакого дерева, что является правильным выбором, когда нужно отфильтровать большой документ и удержать лишь горстку значений. DOM-парсеры в Delphi (DBXJSON, SuperObject и другие) обычно в три-пять раз медленнее, чем подход SAX, для того же содержимого, потому что каждое выделение узла несёт накладные расходы на создание объекта поверх самой работы по разбору
Этот тип следует той же схеме навигации по узлам, что и TALXMLDocument. Минимальное чтение в режиме DOM выглядит так:
MyJsonDoc.LoadFromJSON(AJsonStr, False {dom mode});
MyJsonDoc.ParseOptions := [poAllowComments];
// read scalar values
ShowMessage(MyJsonDoc.ChildNodes['name'].ChildNodes['first'].Text);
ShowMessage(IntToStr(MyJsonDoc.ChildNodes['_id'].Int32));
// iterate an array
for I := 0 to MyJsonDoc.ChildNodes['contribs'].ChildNodes.Count - 1 do
Writeln(MyJsonDoc.ChildNodes['contribs'].ChildNodes[I].Text);
Для режима SAX назначьте анонимную процедуру для OnParseText перед вызовом LoadFromJSON со вторым аргументом, установленным в True. Обратный вызов получает путь узла, имя, значение и TALJSONNodeSubType, который идентифицирует тип JSON (строка, целое число, число с плавающей точкой, логическое значение и так далее). Этот режим не производит выделений в куче для узлов, поэтому он масштабируется на документы произвольно большого размера, не превышая бюджет памяти
TALJsonDocument также читает и записывает BSON нативно; передайте True в качестве флага BSON в LoadFromFile или SaveToFile. Второй вариант, TALJsonDocumentU, внутренне использует UnicodeString (UTF-16) вместо AnsiString (UTF-8) для контекстов, где окружающий код повсюду работает в Unicode
Клиент MongoDB и пул соединений
Драйвер MongoDB от Alcinoe покрывает распространённые операции запросов и нативно обрабатывает пул соединений. Простой клиент, TAlMongoDBClient, открывает и закрывает по одному соединению на операцию. Вариант с пулом, TAlMongoDBConnectionPoolClient, поддерживает набор живых соединений и выдаёт по одному каждому вызывающему потоку из пула, возвращая его по завершении вызова. Эта модель не даёт нескольким потокам блокировать друг друга на установке соединения, что важно всякий раз, когда фоновые рабочие потоки одновременно запрашивают одну и ту же базу данных. Для tailable-курсоров на capped-коллекциях TAlMongoDBTailMonitoringThread отслеживает новые документы и вызывает обратный вызов при их поступлении, что является стандартным паттерном для потоковой передачи журналов или уведомления об изменениях без опроса
Другие компоненты, о которых стоит знать
ALVideoPlayer рендерит видео в TTexture, а не в оверлейное окно, поэтому другие элементы FireMonkey могут располагаться поверх него в Z-порядке. Android-бэкенд использует ExoPlayer, который добавляет поддержку DASH, HLS и SmoothStreaming сверх того, что обрабатывает встроенный MediaPlayer Android. iOS-бэкенд использует AVPlayer с эквивалентной поддержкой HLS
TALWebRTC оборачивает стек WebRTC для одноранговой аудио- и видеосвязи. Он не требует браузера или плагина, и соединение проходит через NAT посредством стандартного согласования ICE/STUN/TURN, которое обрабатывает базовая библиотека
TALStringList заменяет сортировку TStringList на основе AnsiCompareText на не зависящее от локали порядковое сравнение и быструю сортировку, которая до 10 раз быстрее на больших списках. Хешированный вариант, TALHashedStringList, добавляет внутреннюю хеш-таблицу для поиска за O(1) ценой чуть более высоких накладных расходов на малых списках. Обратите внимание, что TALStringList — это 8-битный список AnsiString, а не Unicode; он хорошо подходит для серверного кода, где UTF-8 является рабочей кодировкой, а чистая пропускная способность важнее сравнения с учётом локали
На 64-битной Windows наследие FastCode, давшее многим строковым процедурам Alcinoe их преимущество в скорости (в основном написанный вручную ассемблер x86), не переносится. Сборки Win64 откатываются к реализациям на Pascal, которые заметно медленнее на строковых нагрузках. Проект demo\ALStringBenchMark позволяет измерить разрыв на вашем оборудовании, прежде чем переходить на 64-битную сборку там, где строковая пропускная способность является узким местом
Полный исходный код находится на github.com/Zeus64/alcinoe