Alcinoe je open-source knihovna komponent pro Delphi a C++Builder, spravovaná na GitHubu uživatelem Zeus64. Pokrývá oblasti, které VCL a FireMonkey RTL přenechávají třetím stranám: hardwarově akcelerovaný přehrávač videa (GPU), WebRTC wrapper, nativní editační prvky pro iOS a Android, duální JSON/BSON parser, MongoDB klient s poolingem připojení, ImageMagick wrapper a sadu ovládacích prvků FireMonkey, které se zcela vyhýbají standardnímu vykreslovacímu enginu. Knihovna si vybudovala svou pověst na verzích Rio (10.3.3) a Sydney (10.4.2) a od té doby sleduje každé vydání od Embarcadero. V době psaní tohoto článku je plně kompatibilní s Delphi 11.1 Alexandria a Delphi Athens 12.3
Zahrnutí knihovny Alcinoe do projektu
Instalace se dělí podle jedné otázky: potřebujete design-time podporu pro vizuální prvky Alcinoe? Pokud ne, úplně vynechte BPL. Přidejte {alcinoe_rootdir}\source do vyhledávací cesty knihoven (library search path) projektu a máte hotovo. Každá nevizuální komponenta, včetně parserů, databázových klientů a nástrojů pro práci s řetězci, se kompiluje ze zdrojových kódů bez nutnosti cokoliv registrovat
Pokud potřebujete design-time podporu, je cesta o něco delší. Otevřete Component > Install Packages v Delphi IDE, vyhledejte BPL, které odpovídá vaší verzi (například {alcinoe_rootdir}\lib\bpl\alcinoe\Win32\alexandria\Alcinoe_alexandria.bpl), nainstalujte jej a poté stále přidejte {alcinoe_rootdir}\source do vyhledávací cesty (search path). BPL zaregistruje komponenty; adresář zdrojových kódů je to, co kompilátor najde při kompilaci vašeho projektu
Alcinoe dodává volitelné patche pro zdrojové kódy RTL od Embarcadero. Pokud je chcete, přejděte do {alcinoe_rootdir}\embarcadero\, vyberte podadresář pro vaši verzi a spusťte update.bat. Skript očekává GIT v systémové proměnné PATH a předpokládá výchozí umístění instalace Embarcadero. Získá původní zdrojový kód RTL a aplikuje patche. Jakmile je hotovo, přidejte tento patchovaný adresář zdrojových kódů do vyhledávací cesty projektu, aby jej kompilátor upřednostnil před kopií pouze pro čtení v instalačním stromu Embarcadero. Nic z toho není nutné pro začátek; má to význam, pouze pokud narazíte na chyby, které patche řeší
Android a desugaring proxy D8
Několik komponent Alcinoe (WebRTC, video postavené na ExoPlayeru) závisí na Java knihovnách, které používají vlastnosti jazyka Java 8. Toolchain pro Android, který je dodáván se staršími verzemi Delphi, používá dx.bat pro převod DEX, což na úrovních API pod 26 s těmito bytekódy nedokáže pracovat. Řešením je desugaring, který D8 zpracovává automaticky při přímém vyvolání. Alcinoe poskytuje proxy skript na {alcinoe_rootdir}\tools\D8Proxy\dx.bat, který přesměrovává volání z build systému Delphi do D8, čímž se desugaring stává transparentním. Nahraďte původní dx.bat ve vašem adresáři build-tools pro Android SDK (typicky C:\SDKs\android\build-tools\30.0.3\) tímto proxy. Embarcadero sledovalo základní problém v RSP-24155; novější verze SDK nástrojů jej vyřešily přímo, takže si zkontrolujte, zda váš aktuální toolchain tuto okliku stále potřebuje
Problém s vykreslováním ve FireMonkey a řešení od Alcinoe
Výchozí cyklus překreslování (paint cycle) FireMonkey se stává úzkým hrdlem v uživatelských rozhraních s náročným scrollováním. Jediný TRectangle se zaoblenými rohy může zabrat přibližně 3 ms na překreslení, protože výchozí implementace přepočítává cestu v každém snímku. S 20 takovými viditelnými prvky se to nasčítá na 60 ms na jeden průchod snímku, což omezuje efektivní snímkovou frekvenci hluboko pod hranici pro plynulé scrollování
Alcinoe to řeší pomocí bufferu uloženého na GPU pro každý ovládací prvek. První vykreslení (paint) vykreslí ovládací prvek do TTexture uložené v paměti GPU. Následná překreslení tuto texturu pouze zkopírují (blit) místo toho, aby znovu spouštěla algoritmus vykreslování. Naměřený výsledek na stejném zaobleném obdélníku klesne z přibližně 3 ms na zhruba 0,1 ms. Nad rámec bufferování nahrazuje Alcinoe kreslení cest přes OpenGL u základních tvarů nativními kreslicími API pro Android a iOS, čímž se vyhýbá kompromisu mezi kvalitou a výkonem svázaným s Form.Quality. Příslušnými ovládacími prvky jsou TALRectangle, TALCircle a sada vylepšených kontejnerů rozložení, včetně ScrollBox a TabControl
TALJsonDocument: DOM a SAX v jednom typu
TALJsonDocument je JSON a BSON parser od Alcinoe. Podporuje dva režimy procházení. Režim DOM vytváří in-memory strom objektů, což poskytuje náhodný přístup k libovolnému uzlu za cenu paměti úměrné velikosti dokumentu. Režim SAX spouští události (events), jak parser čte každý token, aniž by si pamatoval jakýkoli strom, což je správná volba, když potřebujete filtrovat rozsáhlý dokument a zachovat pouze hrstku hodnot. DOM parsery v Delphi (DBXJSON, SuperObject a další) jsou pro stejný obsah obvykle třikrát až pětkrát pomalejší než přístup SAX, protože alokace každého uzlu s sebou nese režii vytváření objektů nad rámec samotného parsování
Tento typ sleduje stejný vzorec navigace uzlů jako TALXMLDocument. Minimální čtení pomocí DOM vypadá takto:
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);
Pro režim SAX přiřaďte anonymní proceduru události OnParseText před voláním LoadFromJSON s druhým argumentem nastaveným na True. Callback obdrží cestu uzlu, název, hodnotu a TALJSONNodeSubType, který identifikuje typ JSON (řetězec, celé číslo, reálné číslo, boolean a tak dále). Tento režim neprodukuje žádné alokace na haldě (heap) pro uzly, takže se škáluje na libovolně velké dokumenty, aniž by vyčerpal rozpočet paměti
TALJsonDocument také nativně čte a zapisuje BSON; předejte True jako příznak BSON metodě LoadFromFile nebo SaveToFile. Druhá varianta, TALJsonDocumentU, používá interně UnicodeString (UTF-16) místo AnsiString (UTF-8) pro kontexty, kde okolní kód pracuje s Unicode v celém rozsahu
MongoDB klient a pooling připojení
Ovladač MongoDB v Alcinoe pokrývá běžné dotazovací operace a nativně zpracovává pooling připojení (connection pooling). Jednoduchý klient, TAlMongoDBClient, otevírá a zavírá jedno připojení na operaci. Varianta s poolingem, TAlMongoDBConnectionPoolClient, udržuje sadu aktivních připojení a předává jedno z poolu každému volajícímu vláknu, a po dokončení volání jej vrátí. Tento model zabraňuje tomu, aby se více vláken navzájem blokovalo při sestavování připojení, což je důležité vždy, když pracovníci na pozadí (background workers) dotazují stejnou databázi současně. Pro tailable kurzory v capped kolekcích vlákno TAlMongoDBTailMonitoringThread sleduuje nové dokumenty a spouští callback, když dorazí, což je standardní vzorec pro streamování logů nebo oznamování změn bez nutnosti neustálého dotazování (polling)
Další komponenty, které stojí za to znát
ALVideoPlayer vykresluje video do TTexture namísto překryvného okna (overlay window), takže ostatní ovládací prvky FireMonkey se mohou v Z-indexu nacházet nad ním. Backend pro Android používá ExoPlayer, což přidává podporu DASH, HLS a SmoothStreaming nad rámec toho, co zvládá vestavěný MediaPlayer v Androidu. Backend pro iOS používá AVPlayer s ekvivalentní podporou HLS
TALWebRTC obaluje zásobník (stack) WebRTC pro audio a video spojení peer-to-peer. Nevyžaduje prohlížeč ani plugin a připojení prochází skrz NAT prostřednictvím standardního ICE/STUN/TURN vyjednávání, které zajišťuje podkladová knihovna
TALStringList nahrazuje řazení založené na AnsiCompareText v TStringList ordinálním porovnáním nezávislým na locale a pomocí quicksortu, který je na velkých seznamech až 10krát rychlejší. Hashovaná varianta, TALHashedStringList, přidává interní hashovací tabulku pro vyhledávání s časovou složitostí O(1) za cenu mírně vyšší režie u malých seznamů. Všimněte si, že TALStringList je seznam 8bitových AnsiString řetězců, nikoliv Unicode; hodí se dobře do kódu na straně serveru, kde je UTF-8 pracovním kódováním a hrubá propustnost je důležitější než porovnávání s ohledem na locale
Na 64bitových Windows se dědictví FastCode, které dalo mnoha rutinám pro řetězce v Alcinoe výhodu rychlosti (většinou ručně psaný kód v assembly x86), nepřenáší. Sestavení pro Win64 se spoléhají na implementace v Pascalu, které běží znatelně pomaleji u zátěží náročných na řetězce. Projekt demo\ALStringBenchMark vám umožní změřit rozdíl na vašem hardwaru, než se rozhodnete pro 64bitové sestavení tam, kde je propustnost řetězců úzkým hrdlem
Úplné zdrojové kódy najdete na github.com/Zeus64/alcinoe