GPU Burada Ne Yapar?
Görünmez Ortağın Gücü
UI çizim sürecini anlattığımızda CPU'nun yaptıklarından söz ettik: Measure, Layout, Draw, Canvas komutları. Ama bu sürecin kritik bir ortağından henüz söz etmedik: GPU.
GPU, modern Android cihazlarının görsel akıcılığının sessiz mimarıdır. Onsuz her ekran güncellemesi onlarca kat daha yavaş olurdu.
CPU ve GPU: Farklı Güçler, Farklı İşler
CPU ve GPU temelden farklı tasarım felsefelerine sahiptir.
CPU az sayıda ama son derece güçlü çekirdeğe sahiptir. Karmaşık, sıralı, dallanmalı işlemlerde üstündür. Karar vermek, hesaplamak, koordine etmek — bunlar CPU'nun doğal alanıdır.
GPU ise binlerce küçük çekirdeğe sahiptir. Bu çekirdekler bireysel olarak zayıftır ama hepsi aynı anda çalışabilir. Paralel ve tekrarlayan işlemlerde — özellikle pikselleri hesaplamada — CPU'nun çok önüne geçer.
Bir ekranı render etmek tam olarak bu paralel yapıya uygundur. Milyonlarca piksel aynı anda hesaplanmak zorundadır ve her piksel için yapılan hesap birbirine benzer.
Display List'ten GPU'ya: OpenGL ES ve Vulkan
Main thread çizim komutlarını bir display list'e dönüştürür. Bu liste "şu koordinatta şu renkte bir dikdörtgen çiz, şu noktadan şu noktaya bir çizgi çiz, şu bölgeye şu görseli yerleştir" gibi yüksek seviyeli komutlar içerir.
RenderThread bu listeyi alır ve OpenGL ES ya da daha modern cihazlarda Vulkan aracılığıyla GPU'ya iletir. OpenGL ES ve Vulkan, CPU'nun GPU ile konuştuğu standart grafik API'leridir. Donanım üreticisi kim olursa olsun, GPU bu arayüzü anlayacak şekilde tasarlanmıştır.
GPU komutları aldıktan sonra kendi iç mimarisinde çalışmaya başlar. Geometriyi işler, renkleri hesaplar, gölgeleme efektlerini uygular ve sonucu frame buffer'a yazar.
Frame Buffer ve Ekrana Yolculuk
Frame buffer, ekranda gösterilecek bir sonraki kareyi tutan bellek alanıdır. GPU işini tamamladığında ortaya çıkan piksel dizisi bu buffer'a yazılır.
SurfaceFlinger, Android'in sistem düzeyindeki kompozisyon motorudur. Tüm uygulamaların frame buffer'larını alır ve bunları tek bir ekran görüntüsünde birleştirir. Uygulamanızın penceresi, durum çubuğu, navigasyon çubuğu — bunların hepsi ayrı katmanlardır ve SurfaceFlinger bu katmanları birleştirerek nihai görüntüyü üretir.
Bu nihai görüntü VSync sinyaliyle senkronize edilerek ekran denetleyicisine iletilir ve ekranda görünür hale gelir.
Hardware Acceleration: GPU'yu Devreye Almak
Android 3.0'dan itibaren tüm View çizimleri varsayılan olarak donanım hızlandırmalı (hardware accelerated) çalışır.
Donanım hızlandırma açık olmadığında Canvas komutları CPU üzerinde işlenir ve sonuç bir bitmap'e yazılır. Bu yavaş ve bellek yoğun bir süreçtir. Donanım hızlandırma açık olduğunda Canvas komutları display list'e dönüştürülür ve GPU'ya devredilir. CPU bu işi başlatır ama ağır kısmı GPU üstlenir.
Bazı Canvas operasyonları donanım hızlandırmayı desteklemez. Bu operasyonlar karşılaşıldığında sistem otomatik olarak CPU'ya geri döner. Bu geçiş sessizce yaşanır ama performans maliyeti ciddi olabilir.
Katman Önbelleği: Bir Şeyi İki Kez Çizmemek
GPU'nun bir diğer kritik katkısı katman önbelleğidir (layer caching).
Bir View'ın içeriği değişmiyorsa onu her frame'de yeniden çizmek anlamsızdır. GPU, değişmeyen View'ları önceden render edilmiş bir doku olarak önbellekte tutar. Bir sonraki frame'de bu View'ın yalnızca konumu ya da şeffaflığı değişmişse tam çizim yeniden yapılmaz — önbellekteki doku transform edilerek kullanılır.
Bu mekanizma animasyonları son derece verimli hale getirir. Bir View'ı hareket ettirmek, döndürmek ya da şeffaflaştırmak; View'ı yeniden çizmeden yalnızca GPU üzerinde bir dönüşüm hesabıyla yapılabilir. Bu hesap CPU'da yapılacak tam bir çizimden onlarca kat daha hızlıdır.
setLayerType(LAYER_TYPE_HARDWARE, null) ile bir View'ı açıkça GPU katmanına alabilirsiniz. Ama bu seçim bellek tüketir — her katman GPU belleğinde yer kaplar. Gereksiz yere tüm View'ları katmana almak fayda yerine zarar getirir.
Overdraw: Fazladan Çizimin Bedeli
GPU güçlüdür ama sınırsız değildir. Overdraw bu sınırın en yaygın aşım noktasıdır.
Overdraw, aynı pikselin birden fazla kez çizilmesidir. Düşünün: Bir arka plan var, onun üstünde bir kart var, kartın üstünde bir görsel var, görselin üstünde bir metin var. O metnin bulunduğu piksel dört kez çizilmiştir. Ama kullanıcı yalnızca en üstteki — metni — görür. Alttaki üç çizim wasted GPU işidir.
Hafif overdraw kaçınılmazdır ve zararsızdır. Ama derin katmanlı, şeffaf View'larla dolu karmaşık layout'larda overdraw GPU'yu aşırı yükler ve frame sürelerini uzatır.
Android Developer Options içindeki Show GPU Overdraw seçeneği bu sorunu görselleştirir. Mavi hafif, yeşil orta, kırmızı ciddi overdraw'u gösterir. Kırmızı bölgeler optimize edilmesi gereken noktalardır.
Geliştirici Perspektifinden Bakış
GPU'yu anlamak performans optimizasyonunu çok daha hedefli hale getirir.
Animasyonlarınız için ViewPropertyAnimator ya da ObjectAnimator kullanın. Bunlar GPU katman önbelleğiyle çalışacak şekilde optimize edilmiştir. Özellik animasyonları View'ı yeniden çizmeden GPU dönüşümlerini kullanır.
Karmaşık ve statik View'lar için LAYER_TYPE_HARDWARE kullanımını değerlendirin. Sık değişmeyen ama çizilmesi pahalı View'lar bir kez GPU'ya alınır ve sonraki frame'lerde dönüştürülür.
Ve overdraw'u izleyin. Gereksiz arka plan katmanları, şeffaf View'ların birikimi, iç içe geçmiş ViewGroup'lar — bunlar overdraw'un başlıca kaynaklarıdır ve genellikle kolayca düzeltilebilir.