Technical Article

Knjižnica komponent Alcinoe in združljivost z Delphi 11.1 Alexandria

Alcinoe je odprtokodna knjižnica komponent za Delphi in C++Builder, ki jo na GitHubu vzdržuje Zeus64. Pokriva področja, ki jih VCL in FireMonkey RTL prepuščata tretjim osebam: GPU-pospešeni video predvajalnik, WebRTC ovijalnik, izvorne kontrolne gumbe za urejanje v iOS-u in Androidu, dvonačinski JSON/BSON razčlenjevalnik, odjemalec MongoDB z združevanjem povezav (connection pooling), ovijalnik ImageMagick in zbirko kontrol FireMonkey, ki popolnoma zaobidejo privzeti cikel upodabljanja. Knjižnica si je ugled ustvarila na različicah Rio (10.3.3) in Sydney (10.4.2) ter od takrat sledi vsaki izdaji podjetja Embarcadero. V času pisanja je popolnoma združljiva z Delphi 11.1 Alexandria in Delphi Athens 12.3.

Kako vključiti Alcinoe v projekt

Namestitev se deli glede na eno vprašanje: ali potrebujete podporo med načrtovanjem (design-time support) za vizualne kontrole Alcinoe? Če ne, popolnoma preskočite BPL. Dodajte {alcinoe_rootdir}\source v iskalno pot knjižnic projekta in končali ste. Vsaka nevizualna komponenta, vključno z razčlenjevalniki, podatkovnimi odjemalci in nizi pripomočkov, se prevede iz izvorne kode brez kakršne koli registracije.

Ko potrebujete podporo med načrtovanjem, je pot nekoliko daljša. V Delphi IDE odprite Component > Install Packages, prebrskajte do datoteke BPL, ki ustreza vaši različici (na primer {alcinoe_rootdir}\libpllcinoe\Win32lexandria\Alcinoe_alexandria.bpl), jo namestite in nato še vedno dodajte {alcinoe_rootdir}\source v iskalno pot. BPL registrira komponente; izvorna mapa pa je tisto, kar prevajalnik najde ob prevajanju vašega projekta.

Alcinoe ponuja neobvezne popravke za izvorne kode Embarcadero RTL. Če jih želite, pojdite v {alcinoe_rootdir}\embarcadero\, izberite podmapo za svojo različico in zaženite update.bat. Skript pričakuje GIT v iskalni poti PATH in predvideva privzeto lokacijo namestitve Embarcadero. Prenese izvirno izvorno kodo RTL in uveljavi popravke. Ko je to opravljeno, dodajte to mapo s popravljeno izvorno kodo v iskalno pot projekta, da jo prevajalnik prevzame pred različico samo za branje v namestitveni mapi Embarcadero. Nič od tega ni potrebno za začetek delovanja; pomembno je le, če naletite na hrošče, ki jih ti popravki odpravljajo.

Android in posrednik za odstranjevanje sintaktičnega sladkorja D8 (D8 desugaring proxy)

Več komponent Alcinoe (WebRTC, video na osnovi ExoPlayerja) je odvisnih od knjižnic Java, ki uporabljajo funkcije jezika Java 8. Orodja za Android, ki so priložena starejšim različicam Delphija, za pretvorbo DEX uporabljajo dx.bat, ki ne more obdelati teh bajtnih kod na ravneh API pod 26. Rešitev je desugaring (odstranjevanje sintaktičnega sladkorja), kar D8 samodejno opravi, ko je klican neposredno. Alcinoe ponuja posredniški skript na lokaciji {alcinoe_rootdir} ools\D8Proxy\dx.bat, ki posreduje klice iz gradbenega sistema Delphi v D8, zaradi česar je ta pretvorba pregledna. Zamenjajte izvirno datoteko dx.bat v mapi build-tools vašega Android SDK (običajno C:\SDKsndroiduild-tools.0.3\) s tem posrednikom. Podjetje Embarcadero je to težavo spremljalo pod oznako RSP-24155; kasnejše različice orodij SDK so to težavo odpravile neposredno, zato preverite, ali vaša trenutna orodja še vedno potrebujejo to rešitev.

Težava z upodabljanjem v FireMonkey in rešitev Alcinoe

Privzeti cikel izrisovanja v FireMonkey postane ozko grlo v uporabniških vmesnikih z veliko drsenja. En sam TRectangle z zaobljenimi robovi lahko za ponovni izris potrebuje približno 3 ms, ker privzeta implementacija preračunava pot pri vsakem sličicu (frame). Ko je vidnih 20 takšnih kontrol, to pomeni 60 ms na prehod sličice, kar omeji dejansko hitrost osveževanja precej pod prag za tekoče drsenje.

Alcinoe to rešuje z medpomnilnikom v GPU za vsako kontrolo. Prvo izrisovanje upodobi kontrolo v TTexture, ki se shrani v pomnilnik GPU. Naslednji ponovni izrisi nato prekopirajo (blit) to teksturo, namesto da bi znova izvedli algoritem risanja. Izmerjeni rezultat na istem zaobljenem pravokotniku se zmanjša s približno 3 ms na približno 0.1 ms. Poleg medpomnjenja Alcinoe nadomešča risanje poti OpenGL za osnovne oblike z domačimi risalnimi API-ji za Android in iOS, s čimer se izogne kompromisu med kakovostjo in zmogljivostjo, ki je povezan z lastnostjo Form.Quality. Ustrezne kontrole so TALRectangle, TALCircle ter nabor izboljšanih vsebnikov za postavitev, vključno s ScrollBox in TabControl.

TALJsonDocument: DOM in SAX v enem tipu

TALJsonDocument je razčlenjevalnik JSON in BSON v knjižnici Alcinoe. Podpira dva načina prehajanja. Način DOM zgradi objektno drevo v pomnilniku, kar omogoča naključni dostop do katerega koli vozlišča na račun pomnilnika, ki je sorazmeren z velikostjo dokumenta. Način SAX sproži dogodke, ko razčlenjevalnik bere vsak žeton, ne da bi obdržal drevo v pomnilniku, kar je prava izbira, ko morate filtrirati velik dokument in obdržati le dokaj malo vrednosti. Razčlenjevalniki DOM v Delphiju (DBXJSON, SuperObject in drugi) so običajno tri- do petkrat počasnejši od pristopa SAX za isto vsebino, saj vsaka dodelitev vozlišča prinaša stroške ustvarjanja objektov poleg samega dela razčlenjevanja.

Tip sledi istemu vzorcu navigacije po vozliščih kot TALXMLDocument. Minimalno branje DOM izgleda takole:

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);

Za način SAX pred klicem LoadFromJSON z drugim argumentom, nastavljenim na True, dodelite anonimno proceduro dogodku OnParseText. Povratni klic (callback) prejme pot vozlišča, ime, vrednost in TALJSONNodeSubType, ki identificira tip JSON (niz, celo število, decimalno število, logična vrednost itd.). Ta način ne ustvarja dodelitev kopice (heap allocations) za vozlišča, zato se prilagodi poljubno velikim dokumentom brez preobremenitve pomnilnika.

TALJsonDocument prav tako nativno bere in zapisuje BSON; posredujte True kot zastavico BSON metodi LoadFromFile ali SaveToFile. Druga različica, TALJsonDocumentU, vsebuje UnicodeString (UTF-16) interno namesto AnsiString (UTF-8) za primere, ko okoliška koda deluje v celoti z nizi Unicode.

Odjemalec MongoDB in združevanje povezav (connection pooling)

Gonilnik MongoDB v knjižnici Alcinoe pokriva pogoste poizvedovalne operacije in nativno upravlja z združevanjem povezav. Preprost odjemalec, TAlMongoDBClient, odpre in zapre eno povezavo na operacijo. Različica z združevanjem, TAlMongoDBConnectionPoolClient, vzdržuje niz aktivnih povezav in vsakemu klicateljskemu nitju iz sklada preda eno povezavo ter jo vrne, ko se klic zaključi. Ta model preprečuje, da bi se več nitij medsebojno blokiralo pri vzpostavljanju povezave, kar je pomembno, kadar več delovnih nitij v ozadju hkrati poizveduje po isti bazi podatkov. Za sledilne kazalce (tailable cursors) na omejenih zbirkah (capped collections) TAlMongoDBTailMonitoringThread spremlja nove dokumente in sproži povratni klic, ko ti prispelijo, kar je standarden vzorec za pretakanje dnevnikov ali obveščanje o spremembah brez nenehnega poizvedovanja.

Druge komponente, ki jih je vredno poznati

ALVideoPlayer upodablja video v TTexture namesto v prekrivno okno (overlay window), tako da lahko druge kontrole FireMonkey ležijo nad njim v vrstnem redu Z. Zaledje za Android uporablja ExoPlayer, ki dodaja podporo za DASH, HLS in SmoothStreaming poleg tiste, ki jo ponuja vgrajeni MediaPlayer v sistemu Android. Zaledje za iOS uporablja AVPlayer z enakovredno podporo za HLS.

TALWebRTC ovija sklad WebRTC za neposredno (peer-to-peer) avdio in video komunikacijo. Ne zahteva brskalnika ali vtičnika, povezava pa poteka skozi NAT prek standardnih pogajanj ICE/STUN/TURN, ki jih upravlja spodnja knjižnica.

TALStringList nadomešča razvrščanje TStringList, ki temelji na AnsiCompareText, z ordinalno primerjavo, neodvisno od lokalnih nastavitev, in hitrim razvrščanjem (quicksort), ki je na velikih seznamih do 10-krat hitrejše. Različica z zgostitvijo, TALHashedStringList, dodaja interno zgoščevalno tabelo za iskanje O(1) na račun nekoliko večjih stroškov pri majhnih seznamih. Upoštevajte, da je TALStringList 8-bitni seznam AnsiString in ne Unicode; dobro se obnese v strežniški kodi, kjer je UTF-8 delovni zapis in je surova prepustnost pomembnejša od primerjave, prilagojene lokalnim nastavitvam.

V 64-bitnem okolju Windows se dediščina FastCode, ki je številnim rutinam za nize v Alcinoe dala prednost pri hitrosti (večinoma ročno napisan zbirnik x86), ne prenese. Različice za Win64 se vrnejo k izvedbam v jeziku Pascal, ki delujejo opazno počasneje pri delu z velikim številom nizov. Projekt demo\ALStringBenchMark vam omogoča, da izmerite razliko na vaši strojni opremi, preden se odločite za 64-bitno različico, kjer bi prepustnost nizov lahko bila ozko grlo.

Celotna izvorna koda je na voljo na github.com/Zeus64/alcinoe.