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 ini 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, yang 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 ini dapat dibagi menjadi empat fase utama:
- Prerender: Memproses grafik adegan, menerapkan penyesuaian, dan menyelesaikan tata letak.
- Pembuatan perintah: Mengonversi grafik adegan yang diselesaikan menjadi daftar tampilan yang agnostik backend.
- Rendering: Menjalankan perintah gambar menggunakan mesin grafis Impeller.
- Presentasi: Mengelola framebuffer dan menyinkronkan dengan hardware tampilan.
Gambar 1. Alur grafis HAR.
Fase 1: Prerender
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 pada thread pengurang khusus, terpisah dari loop tampilan utama.
1.1 Dasar DesignCompose
Pipeline HAR dibangun berdasarkan ekosistem DesignCompose.
- Sumber: UI didesain di Figma dan diekspor menggunakan plugin DesignCompose.
- Definisi: Output-nya adalah instance
DesignComposeDefinition, representasi serial desain (node, gaya, varian). - Pengikatan data: Model UI aplikasi menggunakan makro prosedural (misalnya,
#[Design(node = "#speed")]) untuk mengikat kolom struct Rust secara eksplisit ke node bernama tertentu dalam dokumen Figma. Hal ini memungkinkan status aplikasi secara otomatis mendorong properti elemen visual.
Komponen utama dasar ini adalah:
- Pengurang: Bertindak sebagai loop peristiwa pusat, memproses tindakan, dan memperbarui status saat ini. Framework ini menyediakan
DefaultReducer, tetapi implementasi pengurang kustom dapat disediakan jika diperlukan. - Presenter: Menghubungkan status saat ini ke model UI.
PresenterTrait ditentukan oleh crate frameworkharry, dan implementasi referensi (UIModelPresenter) disediakan dalam crateharry-app-core. - Model UI: Membuat penyesuaian berdasarkan status saat ini. Kode model UI dibuat menggunakan makro
DesignDocumentyang disediakan oleh cratederive_customizations. StructUIModeldalam crateharry-app-corememberikan contoh hal ini. - Squoosh: Menyediakan struktur data
SquooshViewdan repositori varian, yang digunakan untuk merender UI sesuai dengan desain. Dokumen desain serial dimuat oleh cratedc_bundledari library DesignCompose dan dikonversi menjadi hierarki structSquooshViewuntuk performa runtime yang efisien.
1.2 Loop pengurang
Pipeline ini didorong oleh tindakan. Framework ini menentukan jenis yang dihitung Actions yang menentukan tindakan internal yang digunakan oleh framework itu sendiri, tetapi juga menyertakan varian CustomAction yang memungkinkan pengguna menentukan tindakan khusus aplikasi tambahan (misalnya, UpdateVehicleSpeed atau ButtonPress).
Framework ini juga menyediakan trait StateAction yang menyederhanakan implementasi tindakan yang memengaruhi status aplikasi dan secara opsional menghasilkan efek samping yang kemudian diteruskan kembali ke aplikasi dari pengurang untuk diproses. Enum CustomActions dalam crate harry-app-core memberikan contoh mendetail tentang hal ini.
Berikut adalah garis besar dasar loop pengurang:
- Pemrosesan tindakan:
Reducermenerima tindakan dan memperbarui status saat ini. Ini adalah data mentah seperti kecepatan saat ini atau lampu peringatan mana yang aktif. Hal ini juga dapat menghasilkan efek samping (misalnya, sinyal memutar bunyi lonceng saat lampu sabuk pengaman berkedip). - Presentasi:
Presentermemetakan status baru ke dalamUIModel.UIModeladalah model tampilan, yang menyimpan data yang diformat secara khusus untuk UI (misalnya, memformat kecepatan "120" ke string "65 mph"). - Pembuatan penyesuaian: Metode
applymodel UI dipanggil untuk membuat kumpulan instanceRenderCustomization. Ini adalah petunjuk eksplisit untuk mengubah desain Figma (misalnya, "Setel teks node #speed ke '65 mph'"). UpdatePolicyuntuk pengoptimalan: Setelah setiap pass prerender, nilaiUpdatePolicyakan ditampilkan, yang menunjukkan kapan update rendering berikutnya diperlukan. Jika tidak ada perubahan status yang tertunda dan tidak ada animasi yang berjalan,UpdatePolicyakan memberi sinyal bahwa tidak ada update lebih lanjut yang segera diperlukan. Dalam kasus seperti itu, Pengurang berhenti membuat daftar tampilan baru, mencegah siklus rendering yang tidak perlu, dan menghemat resource hingga tindakan atau peristiwa baru memicu perubahan.
1.3 Penyerapan tampilan dan inisialisasi repositori
Pipeline dimulai dengan instance DesignComposeDefinition. Ini adalah dokumen desain Figma yang diserialkan oleh DesignCompose ke dalam struktur buffer protokol.
Pemuatan awal: Saat startup, desain utama (yang 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 startup dan penggunaan memori, tampilan tambahan (yang bukan bagian dari hierarki node root awal) dimuat secara lambat dari dokumen hanya jika direferensikan secara eksplisit dan diperlukan oleh logika render (misalnya, selama penyesuaian daftar).
1.4 Pass penyesuaian
Hierarki SquooshView dilalui untuk menerapkan status aplikasi dinamis:
Penggantian varian: Instance komponen diganti dengan varian tertentu (misalnya, mengubah ikon yang mewakili mode drive saat ini dari sport menjadi eco) berdasarkan logika runtime.
Perluasan daftar: Satu item template di Figma diganti dengan daftar turunan dinamis. ID unik baru dibuat untuk 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 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 dalam hierarkiSquooshView.Tata letak teks:
TextHelpermenggunakan implementasi traitLayoutHelperuntuk menghitung metrik teks, wrapping, dan pembentukan. Hal ini membantu memverifikasi bahwa teks mengalir dengan benar dalam batasannya sebelum dirender.
1.7 Dial dan pengukur
Ini adalah langkah khusus untuk UI otomotif.
MeterData: Jika node memiliki data pengukur (yang ditentukan 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.
- Progress bar: Lebar atau tinggi persegi panjang 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 hierarki SquooshView sepenuhnya diselesaikan dan dianimasikan, hierarki tersebut akan dikonversi menjadi urutan perintah gambar 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
TextHelperke entri gambar teks. - Transformasi dan klip: Dikonversi ke pasangan
PushTransform3DdanPopTransform3DatauPushClipRegiondanPopClipRegionuntuk mengelola tumpukan status gambar. - Masking: Dikonversi ke pasangan
PushMaskLayerdanPopMaskLayeruntuk membuat dan memadukan lapisan dengan benar.
- Bentuk dan jalur: Dikonversi ke
Hasil akhirnya adalah instance Vec<DisplayListEntry> yang menjelaskan apa
yang akan digambar, terlepas dari cara menggambarnya.
2.1 Handoff ke looper
Setelah DisplayList dibuat, Pengurang akan meng-wrap-nya dalam instance ViewDescriptor dan mengirimkannya melalui channel MPSC Rust (LooperMessage) ke thread looper. Looper bertanggung jawab atas fase rendering dan tampilan, yang mencegah thread Pengurang memblokir pipeline grafis.
Fase 3: Rendering
DisplayList yang agnostik platform diserahkan ke backend rendering, tempat perintah abstrak diterjemahkan ke dalam petunjuk GPU.
HAR menggunakan Impeller, mesin rendering yang awalnya dibuat untuk Flutter. Impeller dirancang untuk mengatasi masalah gangguan kecepatan frame karena kompilasi shader dengan melakukan prakompilasi kumpulan shader kecil 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 [Introducing Impeller - Flutter's new rendering engine][impeller-video]. Meskipun video ini membahas Flutter, manfaat inti ini secara langsung memberdayakan stack otomotif HAR.
Komponen utama fase rendering adalah:
ImpellerRenderer: Mengonversi daftar tampilan dari fase prerender menjadi perintah rendering Impeller.Impeller Rust API: Meng-wrap 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.Platform FBO yang di-wrap: Alih-alih membuat jendelanya sendiri, Impeller merender ke objek framebuffer OpenGL (FBO) 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. Gambar ini diupload ke tekstur GPU dan dikelola oleh struct
Resourcesinternal.Font: Font TrueType dan OpenType dimuat dan didaftarkan dengan
TypographyContextuntuk rendering 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 Pass render
Loop render membuat instance DisplayList Impeller (jangan sampai
tertukar dengan Vec<DisplayListEntry> yang dibuat oleh fase prerender)
menggunakan DisplayListBuilder:
Menghapus buffer dan menerapkan transformasi global untuk penskalaan DPI dan rotasi tampilan.
Melakukan iterasi melalui item
DisplayListEntryinput:- Status:
save()danrestore()digunakan untuk mendorong dan memunculkan transformasi serta klip region. - 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.
Fase 4: Presentasi
Fase terakhir ini menangani interaksi dengan hardware tampilan untuk menampilkan frame yang dirender. HAR menggunakan jalur rendering langsung yang kuat di Android Automotive OS (AAOS) Software-Defined Vehicle (SDV).
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)
- Hal ini dapat diaktifkan jika diperlukan oleh implementasi atau konfigurasi OpenGL ES yang mendasarinya. Dalam kasus seperti itu, buffer 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 rantai swap grafis biasa.
Buffer depan: Buffer GBM yang dipindai ke layar.
4.2 Rantai swap
Saat swap_buffers dipanggil, HAR akan mengikuti langkah-langkah berikut:
Melakukan blit konten buffer gambar ke buffer render (dengan blit perantara ke buffer penyelesaian, jika diperlukan oleh implementasi).
Memanggil
glFlush()pada konteks GL, dan membuat instanceEGL_SYNC_NATIVE_FENCE_ANDROIDuntuk melacak penyelesaian GPU.Membuat permintaan atom DRM untuk menukar buffer render ke layar. Permintaan ini berisi FD pagar GPU (disebut pagar masuk) untuk mencegah pengontrol tampilan 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 commit permintaan atom menggunakan flag non-blocking, untuk memungkinkan thread utama terus berjalan saat 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 tampilan AAOS SDV, melewati interaksi dengan pengelola jendela seperti SurfaceFlinger (dalam konfigurasi tertentu), sehingga memungkinkan kontrol hardware tampilan yang eksklusif dan berprioritas tinggi.
4.4 Rendering eksternal
HAR mendukung pendelegasian rendering elemen UI tertentu (yang diidentifikasi berdasarkan 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 kumpulan bufferLocalSurface(TextureplusEGLImage) untuk buffering ganda atau tiga kali lipat.SharedSurfaceExternalImage: Wrapper yang aman untuk thread untuk meneruskan pengendaliEGLImageantara layanan eksternal dan perender utama.
4.4.2 Alur kerja
Alur kerja mengikuti urutan ini:
Layanan eksternal dimulai dan mendaftarkan dirinya ke looper utama, yang mengidentifikasi tag Figma (misalnya,
#cluster/3d-car) yang dirender.Layanan ini menunggu sinyal
RenderStartdari looper untuk menyelaraskan rendering-nya dengan sinyal VSYNC tampilan.Di luar layar, layanan ini merender kontennya ke framebuffer yang disediakan oleh
SurfacePool.Layanan ini 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.Perender Impeller utama (Fase 3) menerima gambar eksternal. Alih-alih menyalin data piksel, perender ini mengikat
EGLImageyang mendasarinya langsung ke tekstur dan menggambarnya sebagai bagian dari adegan utama, sehingga mencapai 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 layar. Platform ini diimplementasikan dalam crate crates/reference/platforms/har-platform-linux.
Tidak seperti target AAOS SDV produksi, platform ini tidak menggunakan subsistem direct-rendering dari har-gl-context untuk output tampilan. Sebagai gantinya, platform ini mengandalkan crate Rust OpenGL standar:
Mode berjendela: Menggunakan
winituntuk pengelolaan jendela dan loop peristiwa, sertaglutinuntuk membuat konteks OpenGL ES dan berintegrasi dengan sistem jendela.Mode tanpa layar: Menggunakan crate
har-gl-contextuntuk membuat konteks pbuffer di luar layar 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.