Alcinoe เป็นไลบรารีคอมโพเนนต์แบบโอเพนซอร์สสำหรับ Delphi และ C++Builder ซึ่งดูแลโดย Zeus64 บน GitHub ไลบรารีนี้ครอบคลุมสิ่งที่ VCL และ FireMonkey RTL มักปล่อยให้เป็นหน้าที่ของบุคคลที่สาม เช่น โปรแกรมเล่นวิดีโอที่ใช้การเร่งความเร็วด้วย GPU, wrapper สำหรับ WebRTC, คอนโทรล edit แบบ native สำหรับ iOS และ Android, ตัวแยกวิเคราะห์ JSON/BSON แบบโหมดคู่, ไคลเอ็นต์ MongoDB ที่มี connection pooling, wrapper สำหรับ ImageMagick และชุดคอนโทรล FireMonkey ที่หลีกเลี่ยง pipeline การเรนเดอร์มาตรฐานโดยสิ้นเชิง ไลบรารีนี้สร้างชื่อเสียงตั้งแต่ยุค Rio (10.3.3) และ Sydney (10.4.2) และได้ติดตามการอัปเดตของ Embarcadero ในแต่ละรุ่นตั้งแต่นั้นมา ขณะที่เขียนบทความนี้ ไลบรารีมีความเข้ากันได้อย่างสมบูรณ์กับ Delphi 11.1 Alexandria และ Delphi Athens 12.3
การนำ Alcinoe เข้าสู่โปรเจ็กต์
การติดตั้งขึ้นอยู่กับคำถามเดียว: คุณต้องการการสนับสนุน design-time สำหรับคอนโทรลที่แสดงผลด้วยภาพของ Alcinoe หรือไม่? ถ้าไม่ ให้ข้าม BPL ไปได้เลย เพียงเพิ่ม {alcinoe_rootdir}\source ลงใน library search path ของโปรเจ็กต์ก็เสร็จสิ้น คอมโพเนนต์ที่ไม่ได้ใช้แสดงผลด้วยภาพทั้งหมด รวมถึงตัวแยกวิเคราะห์, ไคลเอ็นต์ฐานข้อมูล และยูทิลิตี้สำหรับสตริง จะคอมไพล์จากซอร์สโค้ดโดยไม่ต้องลงทะเบียนใด ๆ
แต่ถ้าคุณต้องการการสนับสนุน design-time ขั้นตอนจะยาวขึ้นเล็กน้อย ให้เปิด Component > Install Packages ใน Delphi IDE แล้วเรียกดูไฟล์ BPL ที่ตรงกับเวอร์ชันของคุณ (ตัวอย่างเช่น {alcinoe_rootdir}\lib\bpl\alcinoe\Win32\alexandria\Alcinoe_alexandria.bpl) ติดตั้งไฟล์นั้น และคุณยังคงต้องเพิ่ม {alcinoe_rootdir}\source ลงใน search path ไฟล์ BPL จะทำหน้าที่ลงทะเบียนคอมโพเนนต์ ส่วนไดเรกทอรีซอร์สคือสิ่งที่คอมไพเลอร์ใช้ค้นหาเมื่อคอมไพล์โปรเจ็กต์ของคุณ
Alcinoe มีแพตช์เสริมสำหรับซอร์สโค้ด RTL ของ Embarcadero หากคุณต้องการใช้งาน ให้นำทางไปยัง {alcinoe_rootdir}\embarcadero\ เลือกไดเรกทอรีย่อยสำหรับเวอร์ชันของคุณ แล้วเรียกใช้ update.bat สคริปต์นี้ต้องการให้ตั้งค่า GIT ใน PATH และจะถือว่าตำแหน่งการติดตั้งของ Embarcadero เป็นค่าเริ่มต้น สคริปต์จะดึงซอร์ส RTL ต้นฉบับและใช้แพตช์ดังกล่าว เมื่อเสร็จแล้ว ให้เพิ่มไดเรกทอรีซอร์สที่แพตช์แล้วนั้นลงใน search path ของโปรเจ็กต์ของคุณ เพื่อให้คอมไพเลอร์เลือกใช้ก่อนเวอร์ชันแบบอ่านอย่างเดียวในโครงสร้างการติดตั้งของ Embarcadero ทั้งหมดนี้ไม่จำเป็นสำหรับการเริ่มต้นใช้งาน มันจะมีความสำคัญต่อเมื่อคุณพบข้อบกพร่องที่แพตช์เหล่านั้นแก้ไขเท่านั้น
Android และ D8 desugaring proxy
คอมโพเนนต์ของ Alcinoe หลายตัว (WebRTC, วิดีโอที่ใช้ ExoPlayer) พึ่งพาไลบรารี Java ที่ใช้ฟีเจอร์ภาษาของ Java 8 ตัว toolchain สำหรับ Android ที่มาพร้อมกับ Delphi เวอร์ชันเก่าใช้ dx.bat สำหรับการแปลงเป็น DEX ซึ่งไม่สามารถจัดการกับไบต์โค้ดเหล่านั้นบน API ที่มีระดับต่ำกว่า 26 วิธีแก้ไขคือการทำ desugaring ซึ่ง D8 จะจัดการโดยอัตโนมัติเมื่อถูกเรียกใช้โดยตรง Alcinoe มีพร็อกซีสคริปต์ที่ {alcinoe_rootdir}\tools\D8Proxy\dx.bat ซึ่งจะส่งต่อคำสั่งจากระบบ build ของ Delphi ไปยัง D8 ทำให้กระบวนการ desugaring ดำเนินไปอย่างโปร่งใส ให้แทนที่ dx.bat ต้นฉบับในไดเรกทอรี build-tools ของ Android SDK ของคุณ (โดยปกติคือ C:\SDKs\android\build-tools\30.0.3\) ด้วยพร็อกซีตัวนี้ Embarcadero ได้ติดตามปัญหาที่เกี่ยวข้องใน RSP-24155 โดยเวอร์ชันใหม่ ๆ ของ SDK tools ได้แก้ไขปัญหานี้โดยตรงแล้ว ดังนั้นควรตรวจสอบว่า toolchain ปัจจุบันของคุณยังคงต้องใช้วิธีแก้ปัญหานี้อยู่หรือไม่
ปัญหาการเรนเดอร์ของ FireMonkey และคำตอบของ Alcinoe
วัฏจักรการวาด (paint cycle) ค่าเริ่มต้นของ FireMonkey กลายเป็นคอขวดใน UI ที่ต้องมีการเลื่อนหน้าจออย่างหนัก TRectangle แบบมุมโค้งเพียงชิ้นเดียวอาจใช้เวลาในการวาดซ้ำประมาณ 3 มิลลิวินาที เนื่องจากการใช้งานแบบมาตรฐานจะคำนวณเส้นทางใหม่ในทุก ๆ เฟรม หากมีคอนโทรลแบบนี้แสดงผลอยู่ 20 ตัว จะเพิ่มเวลาเป็น 60 มิลลิวินาทีต่อการวาดเฟรมหนึ่งครั้ง ซึ่งจำกัดเฟรมเรตที่มีประสิทธิภาพให้ต่ำกว่าเกณฑ์การเลื่อนหน้าจอที่ลื่นไหลอย่างมาก
Alcinoe แก้ปัญหานี้ด้วยการใช้บัฟเฟอร์ที่อยู่บน GPU สำหรับคอนโทรลแต่ละตัว การวาดครั้งแรกจะเรนเดอร์คอนโทรลลงใน TTexture ที่เก็บไว้ในหน่วยความจำ GPU การวาดซ้ำในครั้งต่อ ๆ ไปจะเป็นการ blit เทกซ์เจอร์นั้นแทนที่จะรันอัลกอริทึมการวาดใหม่ทั้งหมด ผลลัพธ์ที่วัดได้บนสี่เหลี่ยมมุมโค้งแบบเดียวกันลดลงจากประมาณ 3 มิลลิวินาที เหลือประมาณ 0.1 มิลลิวินาที นอกเหนือจากการใช้บัฟเฟอร์แล้ว Alcinoe ยังแทนที่การวาดเส้นทางด้วย OpenGL สำหรับรูปร่างพื้นฐาน ด้วย API การวาดแบบ native ของ Android และ iOS เพื่อหลีกเลี่ยงข้อแลกเปลี่ยนระหว่างคุณภาพและประสิทธิภาพที่ผูกติดอยู่กับ Form.Quality คอนโทรลที่เกี่ยวข้องได้แก่ TALRectangle, TALCircle และคอนเทนเนอร์โครงร่างแบบปรับปรุงที่รวมถึง ScrollBox และ TabControl
TALJsonDocument: รวม DOM และ SAX ไว้ในประเภทเดียว
TALJsonDocument คือตัวแยกวิเคราะห์ JSON และ BSON ของ Alcinoe ซึ่งรองรับโหมดการสืบค้นข้อมูลสองโหมด โหมด DOM จะสร้างโครงสร้างวัตถุในหน่วยความจำ เพื่อให้สามารถเข้าถึงโหนดใด ๆ แบบสุ่มได้ โดยแลกกับการใช้หน่วยความจำที่เป็นสัดส่วนกับขนาดของเอกสาร โหมด SAX จะเรียกใช้เหตุการณ์ตามที่ตัวแยกวิเคราะห์อ่านแต่ละ token โดยไม่เก็บโครงสร้างต้นไม้ไว้ ซึ่งเป็นตัวเลือกที่เหมาะสมเมื่อคุณต้องการกรองเอกสารขนาดใหญ่และเก็บค่าไว้เพียงหยิบมือ ตัวแยกวิเคราะห์แบบ DOM ใน Delphi (เช่น DBXJSON, SuperObject และอื่น ๆ) โดยทั่วไปจะทำงานช้ากว่าวิธี SAX ถึงสามถึงห้าเท่าสำหรับเนื้อหาแบบเดียวกัน เนื่องจากการจัดสรรโหนดแต่ละครั้งจะส่งผลให้มี overhead ของการสร้างอ็อบเจ็กต์เพิ่มเติมจากงานแยกวิเคราะห์โดยตรง
ประเภทนี้ใช้รูปแบบการนำทางโหนดแบบเดียวกับ 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 (สตริง, จำนวนเต็ม, ทศนิยม, บูลีน และอื่น ๆ) โหมดนั้นจะไม่สร้างการจัดสรร heap สำหรับโหนด ดังนั้นจึงสามารถรองรับการขยายตามขนาดเอกสารที่ใหญ่มากได้โดยไม่กินงบประมาณหน่วยความจำจนหมด
TALJsonDocument ยังสามารถอ่านและเขียน BSON แบบ native ได้อีกด้วย เพียงแค่ส่งค่า True เป็นธงของ BSON ไปยัง LoadFromFile หรือ SaveToFile ประเภทที่สองคือ TALJsonDocumentU จะใช้ UnicodeString (UTF-16) ภายใน แทนที่จะเป็น AnsiString (UTF-8) สำหรับบริบทที่โค้ดโดยรอบทำงานในรูปแบบ Unicode ทั้งหมด
ไคลเอ็นต์ MongoDB และ connection pooling
ไดรเวอร์ MongoDB ของ Alcinoe ครอบคลุมการทำงานของการสืบค้นข้อมูลทั่วไปและจัดการ connection pooling แบบ native ไคลเอ็นต์แบบเรียบง่ายคือ TAlMongoDBClient จะเปิดและปิดการเชื่อมต่อหนึ่งครั้งต่อหนึ่งการทำงาน ส่วนตัวแปรที่ใช้ pool คือ TAlMongoDBConnectionPoolClient จะรักษาชุดการเชื่อมต่อที่ใช้งานอยู่และส่งมอบให้กับการเรียกใช้ของเธรดแต่ละตัวจาก pool แล้วรับคืนกลับเมื่อการเรียกใช้เสร็จสิ้น รูปแบบดังกล่าวช่วยป้องกันไม่ให้เธรดหลายตัวบล็อกซึ่งกันและกันในระหว่างการตั้งค่าการเชื่อมต่อ ซึ่งมีความสำคัญทุกครั้งที่ worker ในพื้นหลังสืบค้นข้อมูลจากฐานข้อมูลเดียวกันพร้อมกัน สำหรับ tailable cursors บน capped collections คลาส TAlMongoDBTailMonitoringThread จะเฝ้าดูเอกสารใหม่และเรียกใช้คอลแบ็คเมื่อมีการมาถึง ซึ่งเป็นรูปแบบมาตรฐานสำหรับการสตรีมบันทึกข้อมูลหรือการแจ้งเตือนการเปลี่ยนแปลงโดยที่ไม่ต้องทำ polling
คอมโพเนนต์อื่น ๆ ที่ควรรู้จัก
ALVideoPlayer เรนเดอร์วิดีโอไปยัง TTexture แทนที่จะเป็นโอเวอร์เลย์บนหน้าต่าง ดังนั้นคอนโทรลของ FireMonkey อื่น ๆ จึงสามารถอยู่เหนือมันได้ใน Z-order แบ็กเอนด์ของ Android จะใช้ ExoPlayer ซึ่งเพิ่มการสนับสนุน DASH, HLS และ SmoothStreaming นอกเหนือจากสิ่งที่ MediaPlayer ในตัวของ Android สามารถจัดการได้ แบ็กเอนด์ของ iOS จะใช้ AVPlayer พร้อมกับการสนับสนุน HLS ที่เทียบเท่ากัน
TALWebRTC ห่อหุ้มโครงสร้าง WebRTC สำหรับระบบเสียงและวิดีโอแบบ peer-to-peer โดยไม่ต้องใช้เบราว์เซอร์หรือปลั๊กอิน และการเชื่อมต่อสามารถทะลุผ่าน NAT ด้วยการเจรจามาตรฐาน ICE/STUN/TURN ซึ่งไลบรารีเบื้องหลังจะรับหน้าที่จัดการให้
TALStringList เข้ามาแทนที่การเรียงลำดับแบบ AnsiCompareText ของ TStringList ด้วยการเปรียบเทียบค่าตามลำดับโดยไม่ขึ้นกับ locale และใช้ quicksort ซึ่งเร็วกว่าสูงสุดถึง 10 เท่าในลิสต์ขนาดใหญ่ รุ่นที่ใช้แฮช TALHashedStringList เพิ่มตารางแฮชภายในสำหรับการค้นหาข้อมูลระดับ O(1) โดยแลกกับ overhead ที่สูงขึ้นเล็กน้อยในลิสต์ขนาดเล็ก โปรดทราบว่า TALStringList เป็นลิสต์แบบ AnsiString ขนาด 8 บิต ไม่ใช่ลิสต์แบบ Unicode ซึ่งมันเข้ากันได้ดีกับโค้ดฝั่งเซิร์ฟเวอร์ที่ใช้ UTF-8 เป็นการเข้ารหัสในการทำงาน และ raw throughput มีความสำคัญมากกว่าการเปรียบเทียบที่ต้องตระหนักถึง locale
บนระบบ Windows แบบ 64 บิต มรดกของ FastCode ซึ่งมอบความได้เปรียบด้านความเร็วให้กับรูทีนของสตริงของ Alcinoe มากมาย (ส่วนใหญ่เป็นแอสเซมบลี x86 ที่เขียนด้วยมือ) ไม่ได้ถูกสืบทอดมาด้วย รุ่น build สำหรับ Win64 จะกลับไปใช้การทำงานด้วย Pascal ซึ่งรันช้าลงอย่างเห็นได้ชัดในภาระงานที่เน้นจัดการสตริงจำนวนมาก โปรเจ็กต์ demo\ALStringBenchMark จะช่วยให้คุณวัดช่องว่างของประสิทธิภาพในฮาร์ดแวร์ของคุณได้ ก่อนที่จะตัดสินใจใช้โครงสร้างแบบ 64 บิตเมื่อ throughput ของสตริงอาจกลายเป็นคอขวด
สามารถดูซอร์สโค้ดฉบับเต็มได้ที่ github.com/Zeus64/alcinoe
สำหรับ Delphi Athens 12.3 บทความฉบับนี้ยืนยันความเข้ากันได้ไว้เช่นเดียวกับ Alexandria 11.1 แต่ควรตรวจสอบชื่อไดเรกทอรี BPL และเครื่องมือ Android ของรุ่นที่ติดตั้งจริงก่อนนำแพตช์หรือ proxy ไปใช้