UNITY İLE PERFORMANS PROFILING YAPMAK VE FPS STABİL TUTMAK

Animasyon Akademi11 dk okuma
Frame time grafiği üzerinde 60 FPS hat çizgisi ve Unity logosu ile stopwatch ikonu görseli

Unity ile performans profiling, hangi sistemin fazla zaman yediğini somut verilerle görmenizi ve kare sürelerini tutarlı hâle getirmenizi sağlar; bu yazıda Profiler’ı doğru kurmayı, CPU/GPU darboğazını ayırt etmeyi, GC ve render maliyetlerini azaltmayı ve özel marker’larla sorunlu noktayı bulmayı öğrenerek FPS’i farklı cihazlarda stabil tutmayı öğreneceksiniz.

1. Performans Profiling Mantığını Netleştirmek ve Ölçmek

FPS yerine kare süresi okumayı alışkanlık hâline getirmek

FPS tek başına yanıltıcı olabilir. 60 FPS hedefinde ideal kare süresi yaklaşık 16.67 ms iken, 30 FPS için yaklaşık 33.33 ms beklenir. Oyuncunun hissettiği “stutter” çoğu zaman ortalama FPS’ten değil, kare sürelerinin bir anda zıplamasından kaynaklanır. Bu nedenle önce kare sürelerini (ms) takip etmek, sonra FPS’i yorumlamak daha sağlıklıdır; ekip içinde ortak bir dil ve kalıcı bir süreç oturtmak isterseniz Unity eğitim içeriği sayfasına da göz atabilirsiniz.

Unity Profiler penceresi CPU Usage Rendering Memory Scripts modülleri ve frame time milisaniye grafiği

Ölçümü aynı koşullarda tekrarlamak ve kıyaslamak

Profiling sırasında “aynı sahne, aynı kamera, aynı içerik, aynı kalite ayarı” ile tekrar ölçmek kritik olur. Arada çözünürlük, VSync, kalite profili, gölge mesafesi veya farklı build ayarı değişirse, sonuçlar karşılaştırılamaz. Karar vermeyi kolaylaştırmak için tek bir test senaryosu tanımlamak, ekip içi iletişimi de hızlandırır.

2. Unity Profiler Kurulumu Doğru Yapmak ve Yanıltıcı Veriden Kaçınmak

Editor ile cihaz ölçümünü ayırmak ve build üzerinden doğrulamak

Editor içindeki ölçümler genellikle gerçeğe yakın bir fikir verse de, editor overhead’i nedeniyle CPU tarafında maliyetler şişebilir. Bu yüzden ilk analizleri editor’da yapıp, kritik kararları mutlaka build üzerinde doğrulamak gerekir. Mobil veya konsol hedefliyorsanız, test cihazında ölçüm almak çok daha güvenilir olur.

Development Build ve Autoconnect Profiler ile pratik izleme yapmak

İlk tespitte Development Build açıp Profiler’ı otomatik bağlamak, problemi hızlı yakalamanızı sağlar. Ancak Development Build ek yük getirebilir; bu yüzden iyileştirme sonrası son doğrulamada daha gerçekçi ayarlarla ölçüm yapmak önemlidir. Hedefiniz “sorunu bulmak” ile “nihai performansı onaylamak” süreçlerini ayrı düşünmek olmalıdır.

Unity Frame Debugger paneli draw call listesi SetPass calls ve overdraw view görüntüsü

3. CPU ve GPU Darboğazını Ayırt Etmek ve Stabil Tutmak

Önce darboğazın kimde olduğunu belirlemek

Stabil FPS için en temel soru şudur: CPU mu sınırlıyor, GPU mu? Profiler’da ana thread süreleri yüksekse CPU darboğazı, GPU zamanı baskınsa GPU darboğazı düşünülür. Ayrıca render thread, job worker thread’ler ve “WaitForTargetFPS” gibi beklemeler de doğru yorumlanmalıdır. Örneğin VSync açıksa CPU, GPU’nun yetişmesini bekliyor gibi görünebilir; bu durumda darboğazı yanlış teşhis edebilirsiniz.

Kare süresi dalgalanmasının kök nedenini izole etmek

FPS’in bazen düşmesi yerine sürekli “iniş çıkış” yapması, genellikle şu üç kaynaktan gelir: (1) periyodik iş (garbage collection, asset streaming, ışık bake önbelleği vb.), (2) sahneye bağlı “burst” maliyetler (çok sayıda Instantiate/Destroy, partikül patlaması), (3) kamera/LOD/occlusion gibi sistemlerin anlık yük değişimi. Bu üç kaynağı tek tek izole ederek test etmek, problemi kalıcı biçimde çözmeye yardım eder.

4. GC, Bellek Tahsisi ve Spike Sorunlarını Azaltmak

Allocation’ları takip etmek ve gereksiz tahsisleri engellemek

“GC Alloc” kolonunda gördüğünüz tahsisler, kısa vadede FPS’i düşürmeyebilir; fakat birikince GC tetiklenir ve ani spike üretir. Özellikle Update içinde string birleştirme, LINQ, boxing, sık instantiate edilen geçici listeler ve gereksiz ToArray çağrıları tahsisi artırır. Kodda küçük düzenlemelerle tahsisleri azaltmak, akıcılığı belirgin şekilde artırır.

Object pooling yaklaşımını sistematikleştirmek ve standardize etmek

Sürekli Instantiate/Destroy yapmak hem CPU’da maliyet yaratır, hem de bellek tarafında baskı oluşturur. Object pooling, özellikle mermi, düşman, UI öğesi ve partikül gibi sık doğup ölen objelerde büyük fayda sağlar. Aşağıdaki örnek, basit bir pooling yaklaşımını göstermektedir:

using System.Collections.Generic;
using UnityEngine;

public class SimplePool : MonoBehaviour
{
    [SerializeField] private GameObject prefab;
    [SerializeField] private int warmupCount = 32;

    private readonly Queue<GameObject> pool = new Queue<GameObject>();

    void Awake()
    {
        for (int i = 0; i < warmupCount; i++)
        {
            var go = Instantiate(prefab);
            go.SetActive(false);
            pool.Enqueue(go);
        }
    }

    public GameObject Get(Vector3 pos, Quaternion rot)
    {
        GameObject go = pool.Count > 0 ? pool.Dequeue() : Instantiate(prefab);
        go.transform.SetPositionAndRotation(pos, rot);
        go.SetActive(true);
        return go;
    }

    public void Release(GameObject go)
    {
        go.SetActive(false);
        pool.Enqueue(go);
    }
}

Bu yapıyı ekip standardı hâline getirmek, herkesin farklı pooling mantığı yazmasını engeller. Böylece sahneler arası davranış tutarlı kalır ve profiling çıktılarında yorumlama kolaylaşır.

5. Render Maliyetlerini Düşürmek ve GPU Tarafını Rahatlatmak

Draw call, setpass ve overdraw’ı azaltmak

GPU tarafında en sık karşılaşılan problemler; fazla draw call, pahalı shader’lar, yüksek overdraw ve gereksiz post-process maliyetleridir. İlk adım olarak, sahnede gereksiz materyal çeşitliliğini azaltmak ve mümkün olduğunda batching/instancing avantajı sağlayacak şekilde varlıkları düzenlemek işe yarar. Transparan materyaller overdraw’ı artırır; UI ve partikül yoğun sahnelerde bu etki daha da büyür.

Kalite ayarlarını hedef platforma göre kademelendirmek

Gölge çözünürlüğü, shadow distance, anti-aliasing, SSAO, bloom gibi efektler GPU yükünü hızlı artırır. Buradaki strateji, tek bir “en iyi kalite” yerine farklı cihaz seviyeleri için kademeli profil oluşturmak olmalıdır. Örneğin düşük/orta/yüksek profillerle, aynı sahnede GPU zamanını ölçüp hedeflediğiniz kare süresine yaklaşacak şekilde ayarları basamaklandırabilirsiniz.

Visual Studio kod editörü ProfilerMarker tanımı ve Begin End satırları C# script görünümü

6. Özel Profiler Marker Eklemek ve Sorunlu Noktayı Hızla Bulmak

Unity.Profiling ile ölçülebilir bölgeler tanımlamak

Profiler’da bazen “Script” altında kalabalık bir liste görürsünüz ama hangi fonksiyonun neden pahalı olduğunu anlamak zorlaşır. Bu durumda, kritik kod yollarına özel profiler marker eklemek, ekibin aynı noktaya bakmasını sağlar. Aşağıdaki örnek, belirli bir hesaplama bloğunu işaretlemek için kullanılabilir:

using Unity.Profiling;
using UnityEngine;

public class DamageSystem : MonoBehaviour
{
    static readonly ProfilerMarker ApplyDamageMarker =
        new ProfilerMarker("DamageSystem.ApplyDamage");

    public void ApplyDamage(GameObject target, int amount)
    {
        ApplyDamageMarker.Begin();

        // Simulated work
        var health = target.GetComponent<Health>();
        if (health != null)
        {
            health.Value -= amount;
        }

        ApplyDamageMarker.End();
    }
}

Marker’lar sayesinde “hangi çağrı ne kadar sürüyor” sorusu daha net cevaplanır. Ayrıca değişiklik yaptıktan sonra aynı marker üzerinden kıyaslayarak ilerlemek, optimizasyon kararlarını veriyle destekler.

Derin ölçümü sınırlamak ve yanlış alarmları azaltmak

Deep profiling veya aşırı ayrıntılı ölçüm, analiz sırasında yardımcı olsa da performansı ciddi etkileyebilir. Bu yüzden sorunlu bölgeyi bulduktan sonra ölçümü daraltmak ve sadece ilgili marker’lar üzerinden ilerlemek daha verimli olur. Amaç “her şeyi görmek” değil, asıl maliyeti yaratan yolu kısa sürede ortaya çıkarmaktır.

7. Pratik Stabilizasyon Kontrol Listesi Oluşturmak ve Süreçleştirmek

Değişiklikleri küçük tutmak ve etkisini hemen doğrulamak

Bir anda 10 farklı ayarı değiştirmek, hangi değişikliğin fayda sağladığını belirsiz kılar. Bunun yerine küçük, izlenebilir adımlar atmak gerekir. Aşağıdaki liste, günlük akışta pratik bir kontrol yaklaşımı sunar:

  • Ölçüm senaryosunu sabitlemek ve aynı koşullarda test etmek
  • Darboğazı CPU/GPU olarak ayırmak, sonra alt kırılımı incelemek
  • GC Alloc kaynaklarını tespit etmek ve Update içindeki tahsisleri azaltmak
  • Instantiate/Destroy yoğun noktaları pooling ile dönüştürmek
  • Gölge/efekt ayarlarını hedef cihaza göre kademelendirmek
  • Marker ekleyerek kritik fonksiyonları kıyaslanabilir hâle getirmek

Sahne ve içerik üretimini performans hedefleriyle uyumlu tutmak

Stabil FPS, sadece programlama tarafında değil, içerik üretiminde de hedeflenmelidir. Model yoğunluğu, kaplama boyutları, shader karmaşıklığı ve ışık sayısı gibi kararlar, oyun daha baştan “pahalı” mı “verimli” mi olacak sorusunu belirler. Tasarım ve teknik ekip arasında ortak performans hedefi koymak, sonradan yapılan pahalı düzeltmeleri azaltır.

Sonuç olarak Unity ile performans profiling yapmak, “tahmin” yerine “ölçüm” üzerinden ilerlemenizi sağlar. Doğru kurulum, doğru yorum ve küçük ama etkili değişikliklerle kare sürelerini dengede tutabilir, böylece FPS’in farklı cihazlarda daha stabil kalmasını sağlayabilirsiniz.

Sıkça Sorulan Sorular

Unity Profiler’da FPS dalgalanmasının ana sebebini nasıl bulurum?

Önce kare süresini ms cinsinden izleyip darboğazı CPU mu GPU mu diye ayırın. Sonra Profiler’da ana thread, render thread ve GC Alloc göstergelerini kontrol edin. Düzenli aralıklarla gelen spike’lar genellikle GC, streaming veya ani Instantiate/Destroy yükünden kaynaklanır. Tek bir test sahnesini sabitleyip küçük değişiklikler yaparak tekrar ölçmek, sebebi hızlı izole etmenizi sağlar.

Development Build ile ölçüm almak sonuçları ne kadar etkiler?

Development Build, profilleme ve loglama gibi ek yükler getirebildiği için kare sürelerini bir miktar artırabilir. Bu mod, sorunu bulmak için çok faydalıdır; ancak nihai performansı doğrularken daha gerçekçi build ayarlarıyla yeniden ölçüm yapmak gerekir. En iyi yaklaşım, önce Development Build ile problemli bölgeyi yakalayıp ardından aynı senaryoyu release’e yakın koşullarda karşılaştırmaktır.

GC spike’larını azaltmak için en etkili adımlar nelerdir?

Update içinde tahsis üreten kodları azaltmak ilk adımdır: string birleştirme, LINQ ve gereksiz geçici koleksiyonlar sık görülür. Sık doğup ölen objelerde pooling uygulamak, Instantiate/Destroy kaynaklı baskıyı düşürür. Profiler’daki GC Alloc değerlerini takip ederek hangi fonksiyonların tahsis ürettiğini bulun ve aynı test sahnesinde değişiklik sonrası yeniden ölçüm yaparak etkisini doğrulayın.

GPU darboğazında hangi ayarlar en hızlı kazanç sağlar?

Gölge çözünürlüğü ve shadow distance genellikle en hızlı etki eden ayarlardır; ardından anti-aliasing ve ağır post-process efektleri gelir. Transparan yüzeylerin yoğun olduğu sahnelerde overdraw ciddi maliyet yaratır, bu yüzden UI ve partikül kullanımını da gözden geçirmek gerekir. Hedef platforma göre düşük/orta/yüksek kalite profilleri oluşturup GPU zamanını ölçerek kademeli ayarlamak daha stabil sonuç üretir.

Özel profiler marker eklemek ekip içinde ne kazandırır?

Marker eklemek, kritik fonksiyonların süresini doğrudan görünür hâle getirir ve “hangi kod yolu pahalı” tartışmasını veriye bağlar. Böylece farklı geliştiriciler aynı örneğe bakarak yorum yapabilir, optimizasyon sonrası karşılaştırma kolaylaşır. Ayrıca kalabalık Script listesinde kaybolmak yerine, ilgili marker üzerinden ilerlemek zaman kazandırır ve yanlış teşhisi azaltır.

 ANİMASYON AKADEMİ