Kare hızı

Kare hızı API'si, uygulamaların Android platformunu amaçlanan kare hızı konusunda bilgilendirmesini sağlar. Bu API, Android 11 (API düzeyi 30) veya sonraki sürümleri hedefleyen uygulamalarda kullanılabilir. Geleneksel olarak çoğu cihaz yalnızca tek bir ekran yenileme hızını (genellikle 60 Hz) desteklese de bu durum zaman içinde değişti. Birçok cihaz artık 90 Hz veya 120 Hz gibi ek yenileme hızlarını desteklemektedir. Bazı cihazlar kesintisiz yenileme hızı geçişlerini desteklerken bazılarında kısa süreliğine ve genellikle bir saniye süren siyah ekran gösterilir.

API'nin birincil amacı, uygulamaların desteklenen tüm ekran yenileme hızlarından daha iyi yararlanabilmesini sağlamaktır. Örneğin, setFrameRate() çağrısı yapan 24 Hz'lik bir video oynatan bir uygulama, cihazın görüntü yenileme hızını 60 Hz'den 120 Hz'e değiştirmesine neden olabilir. Bu yeni yenileme hızı, 24 Hz videonun titremeden ve sorunsuz bir şekilde oynatılmasını sağlar. Aynı videonun 60 Hz ekranda oynatılmasını sağlamak için 3:2 oranında açılan içerik gerekmez. Bu, daha iyi bir kullanıcı deneyimiyle sonuçlanır.

Temel kullanım

Android, yüzeylere erişmek ve bunları kontrol etmek için çeşitli yöntemler sunduğundan setFrameRate() API'nin çeşitli sürümleri vardır. API'nin her sürümü aynı parametreleri alır ve diğer sürümleriyle aynı şekilde çalışır:

Uygulamanın, setFrameRate() yöntemini güvenli bir şekilde çağırmak için Display.getSupportedModes() çağrısı yapılarak edinilebilecek desteklenen gerçek ekran yenileme hızlarını dikkate alması gerekmez. Örneğin, cihaz yalnızca 60 Hz'i destekliyor olsa bile setFrameRate() uygulamasını, uygulamanızın tercih ettiği kare hızında çağırın. Uygulamanın kare hızıyla daha iyi bir eşleşme sağlamayan cihazlar, mevcut ekran yenileme hızıyla kalır.

setFrameRate() için yapılan bir çağrının ekran yenileme hızında bir değişiklikle sonuçlanıp sonuçlanmadığını görmek için DisplayManager.registerDisplayListener() veya AChoreographer_registerRefreshRateCallback() numaralı telefonu arayarak görüntü değişikliği bildirimlerine kaydolun.

setFrameRate() çağrılırken, tam sayıya yuvarlamak yerine tam kare hızında geçmeniz önerilir. Örneğin, 29,97 Hz'de kaydedilmiş bir video oluştururken 30'a yuvarlamak yerine 29,97 değerini iletin.

Video uygulamalarında, uygulamanın eşleşmeyen bir ekran yenileme hızına uyum sağlamak için açılır menüyü kullanacağı (titremeye yol açar) Android platformuna ek bir ipucu vermek için setFrameRate() öğesine geçirilen uyumluluk parametresi Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE olarak ayarlanmalıdır.

Bazı durumlarda video yüzeyi kare gönderme işlemini durdurur, ancak bir süre için ekranda görünmeye devam eder. Yaygın senaryolar arasında oynatmanın videonun sonuna ulaşması veya kullanıcının oynatmayı duraklatması yer alır. Böyle durumlarda, yüzeyin kare hızı ayarını tekrar varsayılan değere getirmek için kare hızı parametresi 0 olarak ayarlanmış setFrameRate() çağrısı yapın. Yüzeyi yok ederken veya kullanıcı farklı bir uygulamaya geçtiği için yüzey gizlendiğinde kare hızı ayarını bu şekilde silmek gerekli değildir. Kare hızı ayarını, yalnızca yüzey kullanılmadan görünür kaldığında temizleyin.

Sorunsuz olmayan kare hızı anahtarı

Bazı cihazlarda yenileme hızı değişiminde bir iki saniye siyah ekran gibi görsel kesintiler yaşanabilir. Bu durum genellikle set üstü kutularda, TV panellerinde ve benzer cihazlarda ortaya çıkar. Varsayılan olarak Android çerçevesi, bu tür görsel kesintileri önlemek için Surface.setFrameRate() API çağrıldığında modları değiştirmez.

Bazı kullanıcılar uzun videoların başında ve sonunda görsel kesintilerin olmasını tercih eder. Bu, ekranın yenileme hızının video kare hızıyla eşleşmesini sağlar ve film oynatma için 3:2 çekme titremesi gibi kare hızı dönüştürme yapılarını önler.

Bu nedenle, hem kullanıcı hem de uygulamalar etkinleştirilirse kesintisiz olmayan yenileme hızı anahtarları etkinleştirilebilir:

Filmler gibi uzun süreli videolar için her zaman CHANGE_FRAME_RATE_ALWAYS kullanmanızı öneririz. Bunun nedeni, video kare hızını eşleştirmenin avantajının yenileme hızı değiştirilirken oluşan kesintiden ağır çıkmasıdır.

Ek öneriler

Sık karşılaşılan senaryolar için bu önerileri uygulayın.

Birden fazla yüzey

Android platformu, farklı kare hızı ayarlarına sahip birden fazla yüzeyin bulunduğu senaryoları doğru şekilde yönetecek şekilde tasarlanmıştır. Uygulamanızda farklı kare hızlarına sahip birden fazla yüzey varsa her yüzey için doğru kare hızıyla setFrameRate() öğesini çağırın. Cihaz, bölünmüş ekran veya pencere içinde pencere modunu kullanarak aynı anda birden fazla uygulamayı çalıştırıyor olsa bile, her uygulama kendi yüzeyleri için setFrameRate() hizmetini güvenle çağırabilir.

Platformun, uygulamanın kare hızı değişmez

Cihaz, uygulamanın setFrameRate() çağrısında belirttiği kare hızını desteklese bile ekranı bu yenileme hızına geçirmediği durumlar vardır. Örneğin, daha yüksek öncelikli bir yüzey farklı bir kare hızı ayarına sahip olabilir veya cihaz pil tasarrufu modunda olabilir (pilden tasarruf etmek için ekran yenileme hızına bir kısıtlama ayarlanıyor). Cihaz, normal koşullar altında geçiş yapsa bile ekran yenileme hızını uygulamanın kare hızı ayarına geçirmediğinde uygulama yine de doğru şekilde çalışmalıdır.

Ekranın yenileme hızı, uygulama kare hızıyla eşleşmediğinde nasıl yanıt vereceğine uygulamaya karar verilir. Videoda kare hızı, kaynak videonun kare hızına sabitlenir ve video içeriğini göstermek için açılan menü gerekir. Bir oyun, tercih ettiği kare hızında kalmak yerine ekranın yenileme hızında çalışmayı seçebilir. Uygulama, platformun işlemlerine bağlı olarak setFrameRate() değerine gönderdiği değeri değiştirmemelidir. Uygulamanın, platformun uygulamanın isteğine göre ayarlanmadığı durumları nasıl ele aldığına bakılmaksızın, uygulamanın tercih ettiği kare hızına ayarlı kalmalıdır. Bu şekilde, cihaz koşulları ek ekran yenileme hızlarının kullanılmasına izin verecek şekilde değişirse platform, uygulamanın tercih ettiği kare hızına geçmek için doğru bilgiye sahip olur.

Uygulamanın, ekran yenileme hızında çalışmadığı veya çalışmadığı durumlarda, uygulama, sunum zaman damgalarını ayarlamak için platformun mekanizmalarından birini kullanarak her kare için sunum zaman damgalarını belirtmelidir:

Bu zaman damgalarının kullanılması, platformun bir uygulama çerçevesini çok erken göstermesini durdurur. Bu durum, gereksiz titremelere yol açar. Çerçeve sunusu zaman damgalarının doğru kullanımı biraz zordur. Oyunlarda titremeyi önleme hakkında daha fazla bilgi için kare ilerleme hızı kılavuzumuza bakın ve Android Frame Pacing kitaplığını kullanmayı düşünün.

Bazı durumlarda platform, uygulamanın setFrameRate() özelliğinde belirtilen kare hızının katlarına geçiş yapabilir. Örneğin, bir uygulama 60 Hz ile setFrameRate() öğesini çağırabilir ve cihaz ekranı 120 Hz değerine ayarlayabilir. Bunun nedeni, başka bir uygulamanın yüzeyinin 24 Hz kare hızı ayarına sahip olmasıdır. Bu durumda, ekranı 120 Hz'de çalıştırmak hem 60 Hz yüzeyin hem de 24 Hz yüzeyinin aşağı çekmeden çalışabilmesini sağlar.

Ekran, uygulamanın kare hızının katlarından birinde çalışırken uygulama, gereksiz titremeyi önlemek için her kare için sunum zaman damgaları belirtmelidir. Oyunlarda, Android Frame Pacing kitaplığı çerçeve sunumu zaman damgalarını doğru bir şekilde ayarlamak için yararlı olur.

setFrameRate() ile PreferredDisplayModeId karşılaştırması

WindowManager.LayoutParams.preferredDisplayModeId, uygulamaların kare hızlarını platforma belirtebileceği bir başka yöntemdir. Bazı uygulamalar, ekran çözünürlüğü gibi diğer ekran modu ayarlarını değiştirmek yerine yalnızca ekran yenileme hızını değiştirmek ister. Genel olarak preferredDisplayModeId yerine setFrameRate() kullanın. Uygulamanın belirli bir kare hızına sahip bir mod bulmak için görüntü modları listesinde arama yapması gerekmediğinden setFrameRate() işlevinin kullanımı daha kolay.

setFrameRate(), farklı kare hızlarında birden fazla yüzeyin çalıştığı senaryolarda platforma uyumlu bir kare hızı seçmek için daha fazla fırsat sunar. Örneğin, Pixel 4'te iki uygulamanın bölünmüş ekran modunda çalıştığı, bir uygulamanın 24 Hz'lik bir video oynattığı, diğerinin ise kullanıcıya kaydırılabilir bir liste gösterdiği bir senaryoyu düşünün. Pixel 4, 60 Hz ve 90 Hz olmak üzere iki ekran yenileme hızını destekler. preferredDisplayModeId API kullanıldığında video yüzeyi 60 Hz veya 90 Hz değerini seçmek zorunda kalır. 24 Hz ile setFrameRate() çağrıldığında, video yüzeyi kaynak videonun kare hızı hakkında platforma daha fazla bilgi verir ve platformun ekran yenileme hızı için bu senaryoda 60 Hz'den daha iyi olan 90 Hz değerini seçebilmesini sağlar.

Ancak setFrameRate() yerine preferredDisplayModeId değerinin kullanılması gereken aşağıdaki gibi senaryolar vardır:

  • Uygulama, çözünürlük veya diğer görüntü modu ayarlarını değiştirmek isterse preferredDisplayModeId işlevini kullanın.
  • Platform yalnızca mod geçişi hafifse ve kullanıcının bunu fark etme olasılığı düşükse setFrameRate() çağrısına yanıt olarak görüntü modlarını değiştirir. Uygulama, yoğun mod anahtarı gerektirse bile (örneğin, bir Android TV cihazında) ekran yenileme hızını değiştirmeyi tercih ederse preferredDisplayModeId değerini kullanın.
  • Uygulamanın kare hızının birden fazlasında ekranı işleyemeyen uygulamalar (her karede sunum zaman damgalarının ayarlanmasını gerektirir) preferredDisplayModeId kullanmalıdır.

setFrameRate() ve PreferredYenileRate karşılaştırması

WindowManager.LayoutParams#preferredRefreshRate, uygulamanın penceresinde tercih edilen kare hızını ayarlar ve pencere içindeki tüm yüzeyler için geçerli olur. Uygulama, planlayıcıya uygulamanın amaçlanan kare hızı hakkında daha iyi bir ipucu vermek için cihazın desteklenen yenileme hızlarına bakılmaksızın setFrameRate()'e benzer şekilde tercih edilen kare hızını belirtmelidir.

preferredRefreshRate, setFrameRate() kullanan yüzeyler için yoksayılır. Genel olarak mümkünse setFrameRate() kullanın.

PreferredYenileRate ve PreferredDisplayModeId karşılaştırması

Uygulamalar yalnızca tercih edilen yenileme hızını değiştirmek istiyorsa preferredDisplayModeId yerine preferredRefreshRate kullanılması tercih edilir.

setFrameRate() işlevini çok sık çağırmaktan kaçınma

setFrameRate() çağrısı, performans açısından çok maliyetli olmasa da uygulamalar her karede veya saniyede birden çok kez setFrameRate() çağrısı yapmaktan kaçınmalıdır. setFrameRate() için yapılan çağrılar büyük olasılıkla ekran yenileme hızında değişikliğe neden olur. Bu da geçiş sırasında kare düşüşüne neden olabilir. Doğru kare hızını önceden belirlemeniz ve setFrameRate() adlı kullanıcıyı bir kez aramanız gerekir.

Oyunlar veya diğer video dışı uygulamalar için kullanım

setFrameRate() API'nin birincil kullanım alanı video olsa da diğer uygulamalar için de kullanılabilir. Örneğin, 60 Hz'den yüksek çalışmayacak şekilde çalışan bir oyun (güç kullanımını azaltmak ve daha uzun oyun oturumları elde etmek için) Surface.setFrameRate(60, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT) çağırabilir. Bu şekilde, varsayılan olarak 90 Hz hızda çalışan bir cihaz, oyun etkinken 60 Hz hızda çalışır. Bu sayede, ekran 90 Hz'de çalışırken oyun 60 Hz'de çalışırsa titremeyi önler.

FRAME_RATE_COMPATIBILITY_FIXED_SOURCE kullanımı

FRAME_RATE_COMPATIBILITY_FIXED_SOURCE yalnızca video uygulamaları için tasarlanmıştır. Video dışı kullanım için FRAME_RATE_COMPATIBILITY_DEFAULT kullanın.

Kare hızını değiştirmek için strateji seçme

  • Filmler gibi uzun süreli videoları gösteren uygulamaların setFrameRate(fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, CHANGE_FRAME_RATE_ALWAYS) çağrısı yapmasını kesinlikle öneririz. Burada fps, videonun kare hızıdır.
  • Video oynatmanın birkaç dakika veya daha kısa sürmesini beklediğiniz durumlarda, setFrameRate() uygulamasını CHANGE_FRAME_RATE_ALWAYS ile arayan uygulamaları kesinlikle önermeyiz.

Video oynatma uygulamaları için örnek entegrasyon

Video oynatma uygulamalarında yenileme hızı anahtarlarını entegre etmek için aşağıdaki adımları öneririz:

  1. changeFrameRateStrategy belirleyin:
    1. Film gibi uzun süreli bir video oynatıyorsanız MATCH_CONTENT_FRAMERATE_ALWAYS işlevini kullanın.
    2. Fragman taşıma gibi kısa bir video oynatıyorsanız CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS kullanın.
  2. changeFrameRateStrategy değeri CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS ise 4. adıma gidin.
  3. Aşağıdaki bilgilerin her ikisinin de doğru olup olmadığını kontrol ederek sorunsuz olmayan bir yenileme hızı geçişinin gerçekleşmek üzere olup olmadığını tespit edin:
    1. Mevcut yenileme hızından (C diyelim) videonun kare hızına (V olarak adlandıralım) kesintisiz mod geçişi yapılamaz. C ile V farklıysa ve Display.getMode().getAlternativeRefreshRates V'nin katını içermiyorsa bu durum geçerli olur.
    2. Kullanıcı, sorunsuz olmayan yenileme hızı değişikliklerini etkinleştirdi. Bunu, DisplayManager.getMatchContentFrameRateUserPreference öğesinin MATCH_CONTENT_FRAMERATE_ALWAYS kodu döndürüp döndürmediğini kontrol ederek öğrenebilirsiniz.
  4. Geçiş sorunsuz olacaksa aşağıdakileri yapın:
    1. setFrameRate yazıp fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE ve changeFrameRateStrategy değerlerini iletin. Burada fps, videonun kare hızıdır.
    2. Video oynatmayı başlat
  5. Sorunsuz olmayan bir mod değişikliği yapmak üzereyse aşağıdakileri yapın:
    1. Kullanıcıyı bilgilendirmek için kullanıcı deneyimini gösterin. Kullanıcının bu kullanıcı deneyimini kapatması ve adım 5.d'deki ek gecikmeyi atlaması için bir yöntem uygulamanızı öneririz. Bunun nedeni, önerdiğimiz gecikmenin daha hızlı geçiş süreleri gerektiren ekranlarda gerekenden fazla olmasıdır.
    2. setFrameRate numaralı telefonu arayıp fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE ve CHANGE_FRAME_RATE_ALWAYS bilgilerini iletin. Burada fps, videonun kare hızıdır.
    3. onDisplayChanged geri aramasını bekleyin.
    4. Mod geçişinin tamamlanması için 2 saniye bekleyin.
    5. Video oynatmayı başlat

Yalnızca kesintisiz geçişi destekleyen sözde kod aşağıdaki gibidir:

SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
transaction.setFrameRate(surfaceControl,
    contentFrameRate,
    FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
    CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
transaction.apply();
beginPlayback();

Yukarıda açıklandığı gibi, sorunsuz ve kesintisiz olmayan geçişi destekleyen sözde kod aşağıdaki gibidir:

SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
if (isSeamlessSwitch(contentFrameRate)) {
  transaction.setFrameRate(surfaceControl,
      contentFrameRate,
      FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
      CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
  transaction.apply();
  beginPlayback();
} else if (displayManager.getMatchContentFrameRateUserPreference()
      == MATCH_CONTENT_FRAMERATE_ALWAYS) {
  showRefreshRateSwitchUI();
  sleep(shortDelaySoUserSeesUi);
  displayManager.registerDisplayListener(…);
  transaction.setFrameRate(surfaceControl,
      contentFrameRate,
      FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
      CHANGE_FRAME_RATE_ALWAYS);
  transaction.apply();
  waitForOnDisplayChanged();
  sleep(twoSeconds);
  hideRefreshRateSwitchUI();
  beginPlayback();
}