Halaman ini menjelaskan pipeline grafis lengkap dari perender ketersediaan tinggi (HAR), yang melacak alur data dari dokumen desain Figma ke piksel akhir yang ditampilkan di layar.
Ringkasan
Pipeline mengonversi definisi UI tingkat tinggi menjadi perintah grafis tingkat rendah dan menampilkannya secara efisien di layar hardware. Pipeline ini dirancang untuk aplikasi penting keselamatan otomotif, dengan menekankan rendering deterministik, pengelolaan status yang efisien, dan interaksi yang kuat dengan subsistem grafis platform, seperti Direct Rendering Manager (DRM) dan Generic Buffer Management (GBM).
Pipeline dapat dibagi menjadi empat fase utama:
- Pra-render: Memproses grafik adegan, menerapkan penyesuaian, dan menyelesaikan tata letak.
- Pembuatan perintah: Mengonversi grafik adegan yang diselesaikan menjadi daftar tampilan yang agnostik terhadap backend.
- Rendering: Mengeksekusi perintah gambar menggunakan mesin grafis Impeller.
- Presentasi: Mengelola framebuffer dan menyinkronkan dengan hardware tampilan.
Gambar 1. Alur grafis HAR.
Fase 1: Pra-render
Fase ini mengubah desain Figma statis dan status aplikasi dinamis menjadi hierarki UI dalam memori yang sepenuhnya diselesaikan dan siap untuk dirender. Fase ini berjalan di thread reducer khusus, terpisah dari loop tampilan utama.
1.1 Dasar-dasar DesignCompose
Pipeline HAR dibangun di atas ekosistem DesignCompose.
- Sumber: UI didesain di Figma dan diekspor menggunakan plugin DesignCompose.
- Definisi: Output adalah instance
DesignComposeDefinition, representasi desain yang diserialisasi (node, gaya, varian). - Data binding: Model UI aplikasi menggunakan makro prosedural (misalnya,
#[Design(node = "#speed")]) untuk secara eksplisit mengikat kolom struct Rust ke node bernama tertentu dalam dokumen Figma. Hal ini memungkinkan status aplikasi secara otomatis mendorong properti elemen visual.
Komponen utama fondasi ini adalah:
- Reducer: Bertindak sebagai loop peristiwa pusat, memproses tindakan, dan memperbarui status saat ini. Framework ini menyediakan
DefaultReducer, tetapi implementasi pengurangan kustom dapat diberikan jika diperlukan. - Presenter: Menjembatani status saat ini ke model UI. Trait
Presenterditentukan oleh crate frameworkharry, dan implementasi referensi (UIModelPresenter) disediakan di crateharry-app-core. - Model UI: Menghasilkan penyesuaian berdasarkan status saat ini. Kode model UI dibuat menggunakan makro
DesignDocumentyang disediakan oleh cratederive_customizations. StrukturUIModeldalam crateharry-app-corememberikan contoh ini. - Squoosh: Menyediakan struktur data
SquooshViewdan repositori varian, yang digunakan untuk merender UI sesuai dengan desain. Dokumen desain yang diserialisasi dimuat oleh cratedc_bundledari library DesignCompose dan dikonversi menjadi hierarki structSquooshViewuntuk performa runtime yang efisien.
1.2 Loop pengurang
Pipeline didorong oleh tindakan. Framework ini menentukan jenis yang di-enum Actions yang menentukan tindakan internal yang digunakan oleh framework itu sendiri, tetapi juga menyertakan varian CustomAction yang memungkinkan pengguna menentukan tindakan tambahan khusus aplikasi (misalnya, UpdateVehicleSpeed atau ButtonPress).
Framework ini juga menyediakan trait StateAction yang
menyederhanakan penerapan tindakan yang memengaruhi status aplikasi dan secara opsional
menghasilkan efek samping yang kemudian diteruskan kembali ke aplikasi dari
pengurangan untuk diproses. Enum CustomActions dalam crate harry-app-core
memberikan contoh mendetail tentang hal ini.
Berikut adalah garis besar dasar loop reducer:
- Pemrosesan tindakan:
Reducermenerima tindakan dan memperbarui status saat ini. Ini adalah data mentah seperti kecepatan saat ini atau indikator (lampu peringatan) mana yang aktif. Hal ini juga dapat menimbulkan efek samping (misalnya, sinyal memutar bunyi lonceng saat lampu sabuk pengaman berkedip). - Presentasi:
Presentermemetakan status baru keUIModel.UIModeladalah model tampilan, yang menyimpan data yang diformat secara khusus untuk UI (misalnya, memformat kecepatan "120" menjadi string "65 mph"). - Pembuatan penyesuaian: Metode
applymodel UI dipanggil untuk membuat sekumpulan instanceRenderCustomization. Ini adalah petunjuk eksplisit untuk mengubah desain Figma (misalnya, "Setel teks node #speed ke '65 mph'"). UpdatePolicyuntuk pengoptimalan: Setelah setiap operasi pra-render, nilaiUpdatePolicyakan ditampilkan, yang menunjukkan kapan pembaruan rendering berikutnya diperlukan. Jika tidak ada perubahan status yang tertunda dan tidak ada animasi yang berjalan,UpdatePolicymenandakan bahwa tidak ada update lebih lanjut yang diperlukan saat ini. Dalam kasus tersebut, Reducer berhenti membuat daftar tampilan baru, sehingga mencegah siklus rendering yang tidak perlu dan menghemat resource hingga tindakan atau peristiwa baru memicu perubahan.
1.3 Melihat penyerapan dan inisialisasi repositori
Pipeline dimulai dengan instance DesignComposeDefinition. Ini adalah dokumen desain Figma yang diserialisasi oleh DesignCompose menjadi struktur buffer protokol.
Pemuatan awal: Saat startup, desain utama (ditentukan oleh node root-nya) dikonversi dari
DesignComposeDefinitionmenjadi hierarkiSquooshViewawal. Proses ini hanya dilakukan satu kali.Repositori:
SquooshVariantRepositorymengelola varian komponen yang dapat digunakan kembali dan tampilan yang dimuat pada awalnya.Pemuatan lambat: Untuk meminimalkan waktu mulai dan penggunaan memori, tampilan tambahan (yang bukan bagian dari hierarki node root awal) dimuat secara lambat dari dokumen hanya saat dirujuk dan diperlukan secara eksplisit oleh logika render (misalnya, selama penyesuaian daftar).
1.4 Kartu penyesuaian
Hierarki SquooshView dilalui untuk menerapkan status aplikasi dinamis:
Penggantian varian: Instance komponen diganti dengan varian tertentu (misalnya, mengubah ikon yang merepresentasikan mode mengemudi saat ini dari sport ke eco) berdasarkan logika runtime.
Perluasan daftar: Satu item template di Figma digantikan oleh daftar turunan dinamis. ID unik baru dibuat untuk elemen turunan ini guna memverifikasi identitas yang stabil untuk animasi.
Penggantian teks dan gaya: Konten teks (misalnya, nilai kecepatan) dan gaya (misalnya, opasitas, warna) diperbarui dari status saat ini.
1.5 Resolusi variabel
Token dan variabel desain yang ditentukan di Figma atau secara lokal di aplikasi akan diselesaikan.
- Pengikatan: Properti
SquooshViewyang mereferensikan variabel (seperti warna atau dimensi) diganti dengan nilai konkretnya untuk frame saat ini.
1.6 Penghitungan tata letak
Tata letak dinamis:
DynamicLayoutmenghitung posisi dan ukuran akhir (batas) setiap node di hierarkiSquooshView.Tata letak teks:
TextHelpermenggunakan penerapan traitLayoutHelperuntuk menghitung metrik, pembungkusan, dan pembentukan teks. Hal ini membantu memverifikasi bahwa teks mengalir dengan benar dalam batasannya sebelum dirender.
1.7 Tombol dan pengukur
Ini adalah langkah khusus untuk UI otomotif.
MeterData: Jika node memiliki data meteran (ditetapkan di Figma), geometrinya akan diubah secara dinamis berdasarkanmeter_value(misalnya, kecepatan kendaraan).- Busur: Sudut sapuan disesuaikan.
- Rotasi: Transformasi rotasi dihitung berdasarkan sudut awal dan akhir.
- Status progres: Lebar atau tinggi persegi diskalakan.
- Vektor progres: Panjang jalur vektor disesuaikan.
1.8 Animasi
Perbedaan:
SquooshViewsaat ini dibandingkan denganprevious_squoosh_viewdariPreRenderCache.Interpolasi: Jika properti telah berubah,
Squooshakan membuat interpolator untuk mentransisikan nilai (misalnya, opasitas atau transformasi) secara lancar dari waktu ke waktu.
Fase 2: Pembuatan perintah
Setelah pohon SquooshView diselesaikan dan dianimasikan sepenuhnya, pohon tersebut dikonversi menjadi
urutan perintah menggambar linear.
Komponen utama fase ini adalah crate DisplayList:
generate_dl: Fungsi ini secara rekursif melintasi hierarkiSquooshView.Terjemahan:
- Bentuk dan jalur: Dikonversi ke
DisplayListEntrydengan varianDisplayListAppearanceyang sesuai (misalnya,RectatauPath) - Teks: Dikonversi dengan
TextHelpermenjadi entri gambar teks. - Transformasi dan klip: Dikonversi menjadi pasangan
PushTransform3DdanPopTransform3DatauPushClipRegiondanPopClipRegionuntuk mengelola stack status gambar. - Masking: Dikonversi menjadi pasangan
PushMaskLayerdanPopMaskLayeruntuk membuat dan memadukan lapisan dengan benar.
- Bentuk dan jalur: Dikonversi ke
Hasil akhirnya adalah instance Vec<DisplayListEntry> yang menjelaskan apa
yang harus digambar, terlepas dari cara menggambarnya.
2.1 Handoff ke looper
Setelah DisplayList dibuat, Reducer akan membungkusnya dalam instance
ViewDescriptor dan mengirimkannya melalui saluran MPSC Rust (LooperMessage) ke
thread looper. Looper bertanggung jawab atas fase rendering dan tampilan,
yang mencegah thread Reducer memblokir pipeline grafis.
Fase 3: Rendering
DisplayList yang agnostik terhadap platform diserahkan ke backend rendering,
tempat perintah abstrak diterjemahkan ke dalam instruksi GPU.
HAR menggunakan Impeller, mesin rendering yang awalnya dibuat untuk Flutter. Impeller dirancang untuk memecahkan masalah gangguan kecepatan frame akibat kompilasi shader dengan melakukan pra-kompilasi sekumpulan kecil shader yang efisien pada waktu build. Pendekatan ini, yang dikombinasikan dengan batching yang efektif dan backend yang sangat dioptimalkan, memberikan:
- Performa deterministik: Hampir menghilangkan gangguan kompilasi shader runtime.
- Startup cepat: Mengurangi overhead inisialisasi.
- Jejak kecil: Menghasilkan ukuran biner yang ringkas.
Untuk pengantar menyeluruh tentang arsitektur Impeller, tonton Memperkenalkan Impeller - Mesin rendering baru Flutter. Meskipun video ini membahas Flutter, manfaat inti ini secara langsung mendukung stack otomotif HAR.
Komponen utama fase rendering adalah:
ImpellerRenderer: Mengonversi daftar tampilan dari fase pra-render menjadi perintah rendering Impeller.Impeller Rust API: Membungkus library Impeller untuk digunakan di Rust (crate
impellerdanimpeller-rs-bindgen).TypographyContext: Mengelola pendaftaran font dan pembentukan teks.
3.1 Inisialisasi dan pengelolaan platform
Pembuatan konteks: Perender menginisialisasi instance
impeller::Contextdengan backend OpenGL ES, meneruskan callback untuk menyelesaikan pointer fungsi OpenGL ES dari konteks GL platform.Permukaan FBO yang di-wrap: Alih-alih membuat jendelanya sendiri, Impeller merender ke objek framebuffer (FBO) OpenGL yang ada yang disediakan oleh Fase 4. Hal ini dilakukan dengan memanggil
Surface::create_wrapped_fbo.
3.2 Pengelolaan resource
Gambar: Mendukung format standar dan tekstur terkompresi KTX2. Data ini diupload ke tekstur GPU dan dikelola oleh struktur
Resourcesinternal.Font: Font TrueType dan OpenType dimuat dan didaftarkan dengan
TypographyContextuntuk merender teks.Gambar eksternal: Penanganan khusus untuk tekstur eksternal (misalnya, feed kamera dan perender 3D eksternal) melibatkan pengikatan instance
EGLImageatau tekstur OpenGL eksternal ke objekTextureImpeller untuk rendering tanpa salinan.
3.3 Render pass
Loop render membuat instance DisplayList Impeller (jangan sampai
tertukar dengan Vec<DisplayListEntry> yang dihasilkan oleh fase pra-render)
menggunakan DisplayListBuilder:
Membersihkan buffer dan menerapkan transformasi global untuk penskalaan DPI dan rotasi layar.
Melakukan iterasi melalui item
DisplayListEntryinput:- Status:
save()danrestore()digunakan untuk mendorong dan memunculkan transformasi dan memotong wilayah. - Primitif:
RectdanRoundedRectdigambar menggunakan operasi paint standar. - Jalur: Jalur vektor kompleks (termasuk instance
Arcdinamis) dibuat dan digambar. - Teks:
TextdanStyledTextdirender menggunakanTypographyContext. - Gambar: Gambar standar dan eksternal digambar menggunakan
draw_texture_rect.
- Status:
Mengirimkan daftar tampilan Impeller yang dibuat ke platform menggunakan
surface.draw_display_list(), yang menghasilkan perintah GL yang mendasarinya.Memanggil
swap_buffers()pada konteks yang mendasarinya untuk memicu Fase 4.
Tahap 4: Presentasi
Fase akhir ini menangani interaksi dengan hardware layar untuk menampilkan frame yang dirender. HAR menggunakan jalur rendering langsung yang andal di Software-Defined Vehicle (SDV) Android Automotive OS (AAOS).
Komponen utama fase ini adalah HarDirectRenderingContext (dalam
crate har-gl-context).
4.1 Arsitektur
Lapisan presentasi menggunakan pendekatan buffer ganda dengan target gambar di luar layar:
Buffer gambar: FBO di luar layar tempat Impeller merender adegan.
Buffer penyelesaian (opsional): Buffer tambahan opsional untuk mendukung anti-aliasing multisampel (MSAA)
- Fitur ini dapat diaktifkan jika diperlukan oleh implementasi atau konfigurasi OpenGL ES yang mendasarinya. Dalam kasus tersebut, target ini berfungsi sebagai target perantara untuk menyelesaikan buffer gambar multisampel sebelum blitting (transfer blok bit) ke buffer render.
Buffer render: Buffer generik yang didukung oleh objek GBM, yang sesuai dengan buffer belakang dalam swap chain grafis standar.
Buffer depan: Buffer GBM yang dipindai ke layar.
4.2 Rantai pertukaran
Saat swap_buffers dipanggil, HAR akan mengikuti langkah-langkah berikut:
Menyalin konten buffer gambar ke buffer render (dengan penyalinan perantara ke buffer penyelesaian, jika diperlukan oleh penerapan).
Memanggil
glFlush()pada konteks GL, dan membuat instanceEGL_SYNC_NATIVE_FENCE_ANDROIDuntuk melacak penyelesaian GPU.Membangun permintaan atomik DRM untuk menukar buffer render ke layar. Permintaan ini berisi FD pagar GPU (disebut pagar masuk) untuk mencegah pengontrol layar menampilkan buffer render sebelum GPU selesai menggambar.
Secara bersamaan meminta pagar baru dari DRM (disebut pagar keluar), untuk memberi sinyal saat buffer sebelumnya (buffer depan untuk frame sebelumnya) tidak lagi ditampilkan di layar.
Melakukan permintaan atomik menggunakan tanda non-blocking, untuk memungkinkan thread utama terus berjalan sementara subsistem grafis tetap disinkronkan.
Menyimpan pagar keluar baru dalam konteks sehingga HAR dapat menunggu sinyalnya di awal proses
swap_bufferspada frame berikutnya. Hal ini mencegah GPU menggambar ke buffer yang masih ditampilkan.
4.3 Setelan mode langsung
HAR berinteraksi langsung dengan kernel menggunakan subsistem DRM dan Kernel Mode Setting (KMS) untuk mengonfigurasi resolusi layar AAOS SDV, melewati interaksi dengan pengelola jendela seperti SurfaceFlinger (dalam konfigurasi tertentu), sehingga memungkinkan kontrol eksklusif dan berprioritas tinggi terhadap hardware layar.
4.4 Rendering eksternal
HAR mendukung pendelegasian rendering elemen UI tertentu (diidentifikasi oleh tag di Figma) ke proses atau thread eksternal. Hal ini berguna untuk mengintegrasikan adegan 3D yang kompleks (misalnya, visualisasi mobil ego dari mesin seperti Kanzi atau Unity) atau konten lain yang memerlukan konteks OpenGL khusus.
4.4.1 Komponen utama
HarExternalRenderContext: Konteks EGL di luar layar khusus untuk layanan eksternal.SurfacePool: Mengelola sekumpulan bufferLocalSurface(TextureplusEGLImage) untuk buffering ganda atau tiga kali lipat.SharedSurfaceExternalImage: Wrapper yang aman untuk thread untuk meneruskan handleEGLImageantara layanan eksternal dan perender utama.
4.4.2 Alur Kerja
Alur kerjanya mengikuti urutan berikut:
Layanan eksternal dimulai dan mendaftarkan dirinya dengan looper utama, mengidentifikasi tag Figma (misalnya,
#cluster/3d-car) yang dirender.Layanan menunggu sinyal
RenderStartdari looper untuk menyelaraskan rendering-nya dengan sinyal VSYNC layar.Di luar layar, layanan merender kontennya ke framebuffer yang disediakan oleh
SurfacePool.Layanan memanggil
swap_bufferspada konteksnya, yang memutar kumpulan dan membuat frame yang telah selesai tersedia sebagai instanceSharedSurface.SharedSurfacedi-wrap dalamExternalImagedan dikirim melalui channel MPSC Rust ke looper.Renderer Impeller utama (Fase 3) menerima gambar eksternal. Alih-alih menyalin data piksel, objek ini mengikat
EGLImageyang mendasarinya langsung ke tekstur dan menggambarnya sebagai bagian dari adegan utama, sehingga menghasilkan komposisi tanpa salinan.
4.5 Platform pengembangan dan pengujian (har-platform-linux)
Untuk tujuan pengembangan dan pengujian, aplikasi HAR dapat menargetkan lingkungan desktop Linux standar dan penyiapan tanpa antarmuka. Platform ini diterapkan dalam
crate crates/reference/platforms/har-platform-linux.
Tidak seperti target SDV AAOS produksi, platform ini tidak menggunakan
subsistem direct-rendering dari har-gl-context untuk output tampilan. Sebagai gantinya,
mereka mengandalkan crate OpenGL Rust standar:
Mode berjendela: Menggunakan
winituntuk pengelolaan jendela dan loop peristiwa, sertaglutinuntuk membuat konteks OpenGL ES dan berintegrasi dengan sistem berjendela.Mode tanpa tampilan: Menggunakan crate
har-gl-contextuntuk membuat konteks pbuffer offscreen dengan tampilan EGL default. Hal ini memungkinkan rendering ke buffer di luar layar tanpa memerlukan jendela yang terlihat atau akses hardware tampilan langsung, yang terutama digunakan untuk pengujian otomatis atau pemrosesan backend.