HAR grafik ardışık düzeni

Bu sayfada, yüksek kullanılabilirlik oluşturucunun (HAR) tam grafik işlem hattı ayrıntılı olarak açıklanmakta ve verilerin akışı, bir Figma tasarım belgesinden ekranda görüntülenen son piksellere kadar izlenmektedir.

Genel Bakış

Ardışık düzen, üst düzey kullanıcı arayüzü tanımlarını alt düzey grafik komutlarına dönüştürür ve bunları donanım ekranlarında verimli bir şekilde gösterir. Bu ardışık düzen, otomotivde güvenliğin kritik önem taşıdığı uygulamalar için tasarlanmıştır. Deterministik oluşturma, verimli durum yönetimi ve Direct Rendering Manager (DRM) ile Generic Buffer Management (GBM) gibi platform grafikleri alt sistemleriyle güçlü etkileşim özelliklerini vurgular.

İşlem hattı dört ana aşamaya ayrılabilir:

  1. Önceden oluşturma: Sahne grafiğini işleme, özelleştirmeleri uygulama ve düzeni çözme.
  2. Komut oluşturma: Çözümlenen sahne grafiğini arka uçtan bağımsız bir görüntüleme listesine dönüştürme.
  3. Oluşturma: Impeller grafik motorunu kullanarak çizim komutlarını yürütme.
  4. Sunu: Çerçeve arabelleklerini yönetme ve ekran donanımıyla senkronize etme.

HAR Grafikleri Akışı

Şekil 1. HAR grafikleri akışı.

1. aşama: Önceden oluşturma

Bu aşamada statik Figma tasarımı ve dinamik uygulama durumu, tamamen çözümlenmiş ve oluşturmaya hazır bir bellek içi kullanıcı arayüzü ağacına dönüştürülür. Bu aşama, ana ekran döngüsünden ayrı olarak özel bir azaltıcı iş parçacığında çalışır.

1.1 DesignCompose temeli

HAR ardışık düzeni, DesignCompose ekosistemi üzerine kurulmuştur.

  • Kaynak: Kullanıcı arayüzü Figma'da tasarlanır ve DesignCompose eklentisi kullanılarak dışa aktarılır.
  • Tanım: Çıkış, tasarımın (düğümler, stiller, varyantlar) serileştirilmiş bir temsili olan DesignComposeDefinition örneğidir.
  • Veri bağlama: Uygulamanın kullanıcı arayüzü modeli, Rust yapı alanlarını Figma belgesindeki belirli adlandırılmış düğümlere açıkça bağlamak için prosedürel makrolar (ör. #[Design(node = "#speed")]) kullanır. Bu sayede uygulama durumu, görsel öğelerin özelliklerini otomatik olarak yönlendirebilir.

Bu temelin temel bileşenleri şunlardır:

  • Reducer: İşlemleri işleyip mevcut durumu güncelleyerek merkezi etkinlik döngüsü olarak işlev görür. Çerçeve DefaultReducer sağlar ancak gerekirse özel bir azaltıcı uygulama sağlanabilir.
  • Sunucu: Mevcut durumu kullanıcı arayüzü modeline bağlar. Presenter özelliği harry çerçeve sandığı tarafından belirtilir ve harry-app-core sandığında bir referans uygulaması (UIModelPresenter) sağlanır.
  • Kullanıcı arayüzü modeli: Mevcut duruma göre özelleştirmeler oluşturur. Kullanıcı arayüzü modeli kodu, DesignDocument sandığı tarafından sağlanan derive_customizations makrosu kullanılarak oluşturulur. harry-app-core paketindeki UIModel yapısı bunun bir örneğini sunar.
  • Squoosh: Tasarıma göre kullanıcı arayüzünü oluşturmak için kullanılan SquooshView veri yapısını ve varyant deposunu sağlar. Serileştirilmiş bir tasarım belgesi, DesignCompose kitaplığındaki dc_bundle sandığı tarafından yüklenir ve verimli çalışma zamanı performansı için SquooshView yapıları ağacına dönüştürülür.

1.2 Azaltıcı döngü

Ardışık düzen, işlemler tarafından yönlendirilir. Çerçeve, Actions çerçevenin kendisi tarafından kullanılan dahili işlemleri tanımlayan numaralandırılmış türü belirtir ancak kullanıcıların uygulamaya özel ek işlemler (örneğin, UpdateVehicleSpeed veya ButtonPress) tanımlamasına olanak tanıyan bir CustomAction varyantını da içerir.

Çerçeve, uygulama durumunu etkileyen işlemlerin uygulanmasını basitleştiren ve isteğe bağlı olarak, işlenmek üzere indirgeyiciden uygulamaya geri aktarılan yan etkiler oluşturan StateAction özelliğini de sağlar. harry-app-core paketindeki CustomActions enum'ı bu konuda ayrıntılı bir örnek sunar.

Bu, azaltıcı döngüsünün temel bir ana hattıdır:

  • İşlem işleme: Reducer bir işlem alır ve mevcut durumu günceller. Bu, mevcut hız veya hangi gösterge lambalarının (uyarı ışıkları) etkin olduğu gibi ham verilerdir. Bu durum, yan etkilere de neden olabilir (ör. emniyet kemeri ışığı yanıp söndüğünde bir sinyal ses çıkarabilir).
  • Sunum: Presenter, yeni durumu UIModel ile eşler. UIModel, kullanıcı arayüzü için özel olarak biçimlendirilmiş verileri tutan bir görünüm modelidir (örneğin, "120" hızını "65 km/sa" dizesine biçimlendirme).
  • Özelleştirme oluşturma: apply yöntemi, bir dizi RenderCustomization örneği oluşturmak için kullanıcı arayüzü modelinde çağrılır. Bunlar, Figma tasarımını değiştirmeye yönelik açık talimatlardır (ör. "Düğüm #speed metnini "65 mph" olarak ayarla").
  • Optimizasyon için UpdatePolicy: Her önceden oluşturma geçişinden sonra, bir UpdatePolicy değeri döndürülür. Bu değer, bir sonraki oluşturma güncellemesinin ne zaman gerekli olduğunu gösterir. Bekleyen durum değişikliği yoksa ve animasyon çalışmıyorsa, UpdatePolicy başka güncellemeye gerek olmadığını gösterir. Bu gibi durumlarda, Reducer yeni görüntüleme listeleri oluşturmayı durdurur. Böylece, yeni bir işlem veya etkinlik değişikliği tetikleyene kadar gereksiz oluşturma döngüleri önlenir ve kaynaklar korunur.

1.3 Alma ve depo başlatma işlemlerini görüntüleme

İşlem hattı, DesignComposeDefinition örneğiyle başlar. Bu, DesignCompose tarafından protokol arabelleği yapısına dönüştürülen Figma tasarım dokümanıdır.

  • İlk yükleme: Başlangıçta, ana tasarım (kök düğümüyle belirtilir) DesignComposeDefinition biçiminden ilk SquooshView ağacına dönüştürülür. Bu tek seferlik bir işlemdir.

  • Depo: SquooshVariantRepository Yeniden kullanılabilir bileşen varyantlarını ve başlangıçta yüklenen görünümleri yönetir.

  • Geç yükleme: Başlatma süresini ve bellek kullanımını en aza indirmek için ek görünümler (ilk kök düğüm ağacının parçası olmayanlar) yalnızca açıkça referans verildiğinde ve oluşturma mantığı tarafından ihtiyaç duyulduğunda (ör. liste özelleştirme sırasında) belgeden geç yüklenir.

1.4 Özelleştirme kartı

Dinamik uygulama durumunu uygulamak için SquooshView ağacında gezinilir:

  • Varyant değişimleri: Bileşen örnekleri, çalışma zamanı mantığına göre belirli varyantlarla değiştirilir (ör. mevcut sürüş modunu temsil eden bir simgeyi spor modundan eko moduna değiştirme).

  • Liste genişletme: Figma'daki tek bir şablon öğesi, dinamik bir alt öğe listesiyle değiştirilir. Bu alt öğeler için, animasyonlarda kararlı bir kimlik doğrulamak amacıyla yeni benzersiz kimlikler oluşturulur.

  • Metin ve stil geçersiz kılmaları: Metin içeriği (ör. hız değeri) ve stiller (ör. opaklık, renk) mevcut durumdan güncellenir.

1.5 Değişken çözünürlük

Figma'da veya uygulamada yerel olarak tanımlanan tasarım jetonları ve değişkenler çözümlenir.

  • Bağlama: Değişkenlere (ör. renkler veya boyutlar) referans veren SquooshView özellikleri, mevcut çerçeve için somut değerleriyle değiştirilir.

1.6 Düzen hesaplaması

  • Dinamik düzen: DynamicLayout, SquooshView ağacındaki her düğümün son konumunu ve boyutunu (sınırlarını) hesaplar.

  • Metin düzeni: TextHelper, metin metriklerini, sarmayı ve şekillendirmeyi hesaplamak için LayoutHelper özelliğinin bir uygulamasını kullanır. Bu, metnin oluşturulmadan önce kısıtlamaları dahilinde doğru şekilde aktığını doğrulamaya yardımcı olur.

1.7 Kadranlar ve göstergeler

Bu, otomotiv kullanıcı arayüzleri için özel bir adımdır.

  • MeterData: Bir düğümde ölçüm verileri (Figma'da tanımlanır) varsa geometrisi meter_value'ye (örneğin, araç hızı) göre dinamik olarak değiştirilir.
    • Yaylar: Süpürme açısı ayarlanır.
    • Dönüşler: Dönüşüm, başlangıç ve bitiş açılarına göre hesaplanır.
    • İlerleme çubukları: Dikdörtgenin genişliği veya yüksekliği ölçeklendirilir.
    • İlerleme vektörleri: Vektör yolunun uzunluğu ayarlanır.

1.8 Animation

  • Fark karşılaştırması: Geçerli SquooshView, PreRenderCache tarih aralığındaki previous_squoosh_view ile karşılaştırılır.

  • İnterpolasyon: Özellikler değiştiyse Squoosh, değerlerin (ör. opaklık veya dönüştürme) zaman içinde sorunsuz bir şekilde geçişini sağlamak için interpolasyonlar oluşturur.

2. aşama: Komut oluşturma

SquooshView ağacı tamamen çözülüp animasyon haline getirildikten sonra, çizim komutlarının doğrusal bir dizisine dönüştürülür.

Bu aşamanın temel bileşeni DisplayList sandığıdır:

  • generate_dl: Bu işlev, SquooshView ağacında yinelemeli olarak gezinir.

  • Çeviri:

    • Şekiller ve yollar: Uygun DisplayListAppearance varyantıyla (örneğin, Rect veya Path) DisplayListEntry olarak dönüştürülür.
    • Metin: TextHelper ile metin çizimi girişlerine dönüştürülür.
    • Dönüşümler ve kırpmalar: Çizim durumu yığınını yönetmek için PushTransform3D ve PopTransform3D veya PushClipRegion ve PopClipRegion çiftlerine dönüştürülür.
    • Maskeleme: Katmanları doğru şekilde oluşturmak ve karıştırmak için PushMaskLayer ve PopMaskLayer çiftlerine dönüştürülür.

Sonuç, nasıl çizileceğinden bağımsız olarak ne çizileceğini açıklayan bir Vec<DisplayListEntry> örneğidir.

2.1 Döngü oluşturucuya aktarma

DisplayList oluşturulduktan sonra Reducer, bunu ViewDescriptor örneğine sarar ve Rust MPSC kanalı (LooperMessage) üzerinden looper iş parçacığına gönderir. Looper, oluşturma ve görüntüleme aşamalarından sorumludur. Bu sayede, Reducer iş parçacığının grafik işlem hattını engellemesi önlenir.

3. aşama: Oluşturma

Platformdan bağımsız DisplayList, soyut komutların GPU talimatlarına çevrildiği oluşturma arka ucuna aktarılır.

HAR, başlangıçta Flutter için oluşturulmuş bir oluşturma motoru olan Impeller'ı kullanır. Impeller, derleme sırasında küçük ve verimli bir gölgelendirici grubu önceden derleyerek gölgelendirici derlemesinden kaynaklanan kare hızı aksaklıkları sorununu çözmek için tasarlanmıştır. Bu yaklaşım, etkili toplu işleme ve son derece optimize edilmiş bir arka uçla birlikte şunları sağlar:

  • Belirleyici performans: Çalışma zamanı gölgelendirici derleme hatalarını neredeyse tamamen ortadan kaldırır.
  • Hızlı başlatma: Başlatma ek yükünü azaltır.
  • Küçük yer kaplama: Kompakt bir ikili boyut üretir.

Impeller'ın mimarisine dair kapsamlı bir giriş için [Introducing Impeller - Flutter's new rendering engine][impeller-video] başlıklı videoyu izleyin. Videoda Flutter ele alınsa da bu temel avantajlar, HAR otomotiv yığınını doğrudan destekler.

Oluşturma aşamasının temel bileşenleri şunlardır:

  • ImpellerRenderer: Görüntüleme listesini önceden oluşturma aşamasından Impeller oluşturma komutlarına dönüştürür.

  • Impeller Rust API: Impeller kitaplığını Rust'ta kullanılmak üzere sarmalar (impeller ve impeller-rs-bindgen kasaları).

  • TypographyContext: Yazı tipi kaydını ve metin şekillendirmeyi yönetir.

impeller-video

3.1 Başlatma ve yüzey yönetimi

  • Bağlam oluşturma: Oluşturucu, OpenGL ES arka ucuyla impeller::Context örneğini başlatır ve platformun GL bağlamından OpenGL ES işlev işaretçilerini çözmek için bir geri çağırma iletir.

  • Sarmalanmış FBO yüzeyi: Impeller, kendi penceresini oluşturmak yerine 4. Aşama tarafından sağlanan mevcut bir OpenGL çerçeve arabelleği nesnesine (FBO) oluşturur. Bu işlem, Surface::create_wrapped_fbo çağrılarak yapılır.

3.2 Kaynak yönetimi

  • Görseller: Standart biçimleri ve KTX2 sıkıştırılmış dokuları destekler. Bunlar, GPU dokularına yüklenir ve dahili bir Resources yapısı tarafından yönetilir.

  • Yazı tipleri: TrueType ve OpenType yazı tipleri, metin oluşturma için TypographyContext ile yüklenir ve kaydedilir.

  • Harici resimler: Harici dokular (ör. kamera feed'leri ve harici 3D oluşturucular) için özel işleme, sıfır kopyalı oluşturma için EGLImage örneklerinin veya harici OpenGL dokularının Impeller Texture nesnelerine bağlanmasını içerir.

3.3 Oluşturma kartı

render döngüsü, DisplayListBuilder kullanarak bir Impeller DisplayList örneği oluşturur (önceden oluşturma aşamasında oluşturulan Vec<DisplayListEntry> ile karıştırılmamalıdır):

  1. Arabelleği temizler ve DPI ölçeklendirme ile ekran döndürme için genel dönüşümleri uygular.

  2. Giriş DisplayListEntry öğeleri arasında yinelenir:

    • Durum: save() ve restore(), dönüştürmeleri ve kırpma bölgelerini itip açmak için kullanılır.
    • Temel öğeler: Rect ve RoundedRect, standart boyama işlemleri kullanılarak çizilir.
    • Yollar: Dinamik Arc örnekleri de dahil olmak üzere karmaşık vektör yolları oluşturulur ve çizilir.
    • Metin: Text ve StyledText, TypographyContext kullanılarak oluşturulur.
    • Resimler: Standart ve harici resimler draw_texture_rect kullanılarak çizilir.
  3. Oluşturulan Impeller görüntüleme listesini surface.draw_display_list() kullanarak yüzeye gönderir ve temel GL komutlarını oluşturur.

  4. 4. Aşamayı tetiklemek için temel bağlamda swap_buffers() işlevini çağırır.

4. Aşama: Sunum

Bu son aşamada, oluşturulan kareyi göstermek için ekran donanımıyla etkileşim ele alınır. HAR, Android Automotive OS (AAOS) yazılımla tanımlanmış araçta (SDV) güçlü bir doğrudan oluşturma yolu kullanır.

Bu aşamanın temel bileşeni HarDirectRenderingContext (har-gl-context sandığında) şeklindedir.

4.1 Mimari

Sunum katmanında, ekran dışı çizim hedefiyle çift arabellekli bir yaklaşım kullanılır:

  1. Çizim arabelleği: Impeller'in sahneyi oluşturduğu ekran dışı FBO.

  2. Çözüm arabelleği (isteğe bağlı): Çok örnekli kenar yumuşatma (MSAA) özelliğini desteklemek için isteğe bağlı yardımcı arabellek

    • Bu, temel OpenGL ES uygulaması veya yapılandırması tarafından gerektiğinde etkinleştirilebilir. Bu gibi durumlarda, çok örnekli çizim arabelleğini oluşturma arabelleğine blit (bit blok aktarımı) işleminden önce çözmek için ara hedef olarak kullanılır.
  3. Oluşturma arabelleği: Tipik bir grafik değişim zincirindeki arka arabelleğe karşılık gelen bir GBM nesnesiyle desteklenen genel arabellek.

  4. Ön arabellek: Ekrana taranan GBM arabelleği.

4.2 Zinciri değiştirme

swap_buffers çağrıldığında HAR şu adımları uygular:

  1. Çizim arabelleğinin içeriğini oluşturma arabelleğine aktarır (uygulama tarafından gerekirse çözme arabelleğine ara aktarma ile).

  2. GL bağlamında glFlush() çağrıları yapar ve GPU tamamlanmasını izlemek için EGL_SYNC_NATIVE_FENCE_ANDROID örneği oluşturur.

  3. Render arabelleğini ekranla değiştirmek için bir DRM atomik isteği oluşturur. Bu istek, GPU'nun çizimi bitirmeden önce ekran denetleyicisinin oluşturma arabelleğini göstermesini önlemek için GPU çit FD'sini (in fence olarak adlandırılır) içerir.

  4. Önceki arabellek (önceki kare için ön arabellek) artık ekranda olmadığında sinyal vermek için DRM'den eşzamanlı olarak yeni bir sınır (çıkış sınırı olarak adlandırılır) ister.

  5. Grafik alt sistemleri senkronize kalırken ana iş parçacığının devam etmesini sağlamak için engellemeyen işareti kullanarak atomik isteği işler.

  6. Yeni çıkış çitini bağlamda depolar. Böylece HAR, sonraki karede swap_buffers işleminin başlangıcında sinyal verilmesini bekleyebilir. Bu, GPU'nun hâlâ görüntülenmekte olan bir arabelleğe çizim yapmasını engeller.

4.3 Doğrudan mod ayarı

HAR, ekran çözünürlüğünü AAOS SDV olarak yapılandırmak için DRM ve Kernel Mode Setting (KMS) alt sistemlerini kullanarak doğrudan çekirdekle etkileşime girer. SurfaceFlinger gibi pencere yöneticileriyle etkileşimi (belirli yapılandırmalarda) atlayarak ekran donanımının özel ve yüksek öncelikli kontrolüne olanak tanır.

4.4 Harici oluşturma

HAR, belirli kullanıcı arayüzü öğelerinin (Figma'daki etiketlerle tanımlanır) oluşturulmasını harici işlemlere veya iş parçacıklarına devretmeyi destekler. Bu, karmaşık 3D sahneleri (örneğin, Kanzi veya Unity gibi motorlardan alınan bir ego araba görselleştirmesi) ya da özel bir OpenGL bağlamı gerektiren diğer içerikleri entegre etmek için kullanışlıdır.

4.4.1 Temel bileşenler

  • HarExternalRenderContext: Harici hizmet için özel bir ekran dışı EGL bağlamı.
  • SurfacePool: Çift veya üçlü arabelleğe alma için bir dizi LocalSurface (Texture artı EGLImage) arabelleği yönetir.
  • SharedSurfaceExternalImage: Harici hizmet ile ana oluşturucu arasında EGLImage işleyicilerini geçirmek için iş parçacığı açısından güvenli bir sarmalayıcı.

4.4.2 İş Akışı

İş akışı şu sırayı izler:

  1. Harici hizmet başlatılır ve ana döngüyle kendini kaydeder. Hangi Figma etiketlerini (ör. #cluster/3d-car) oluşturduğunu tanımlar.

  2. Hizmet, oluşturma işlemini ekranın VSYNC sinyaliyle uyumlu hale getirmek için döngücüden RenderStart sinyallerini bekler.

  3. Hizmet, ekran dışında içeriğini SurfacePool tarafından sağlanan bir çerçeve arabelleğine oluşturur.

  4. Hizmet, bağlamında swap_buffers çağrısı yapar. Bu çağrı, havuzu döndürür ve tamamlanan kareyi SharedSurface örneği olarak kullanılabilir hale getirir.

  5. SharedSurface, ExternalImage içine sarmalanır ve Rust MPSC kanalı üzerinden döngücüye gönderilir.

  6. Ana Impeller oluşturucusu (3. Aşama), bilinmeyen kaynaktan gelen resmi alır. Piksel verilerini kopyalamak yerine, temel EGLImage öğesini doğrudan bir dokuya bağlar ve ana sahnenin bir parçası olarak çizerek sıfır kopyalı kompozisyon elde eder.

4.5 Geliştirme ve test platformları (har-platform-linux)

HAR uygulamaları, geliştirme ve test amacıyla standart Linux masaüstü ortamlarını ve başsız kurulumları hedefleyebilir. Bu platformlar crates/reference/platforms/har-platform-linux kasasında uygulanır.

Üretim AAOS SDV hedefinin aksine, bu platformlar ekran çıkışı için har-gl-context'nin direct-rendering alt sistemini kullanmaz. Bunun yerine, standart Rust OpenGL paketlerini kullanırlar:

  • Pencere modu: Pencere yönetimi ve etkinlik döngüleri için winit, OpenGL ES bağlamları oluşturmak ve pencere sistemiyle entegre etmek için glutin kullanılır.

  • Gözetimsiz mod: Varsayılan EGL ekranıyla ekran dışı pbuffer bağlamı oluşturmak için har-gl-context kasasını kullanır. Bu, öncelikle otomatik test veya arka uç işleme için kullanılan, görünür bir pencereye ya da doğrudan ekran donanım erişimine gerek kalmadan ekran dışı bir arabelleğe oluşturmayı sağlar.