Dokumen ini menunjukkan cara mengoptimalkan performa game dengan menggunakan alat untuk mengidentifikasi dan menyelesaikan bottleneck CPU dan GPU.
Pengoptimalan CPU
Jika analisis menunjukkan bahwa game terikat CPU, penyelidikan lebih lanjut sangat penting. Hal ini memerlukan identifikasi thread atau API tertentu yang menyebabkan hambatan dan mengurangi FPS.
Untuk pengoptimalan CPU, solusi universal umumnya tidak efektif. Sebagai gantinya, Anda harus mengidentifikasi beban kerja yang paling berat berdasarkan game atau adegan, lalu mengoptimalkan logika dan fungsi yang relevan.
Alat perekaman waktu game engine
Alat berikut dapat membantu analisis ini:
Insight tidak nyata
Dalam project Unreal Engine, Alat Unreal Insight memfasilitasi analisis informasi rekaman aktivitas waktu untuk setiap thread yang membentuk frame.
Sebagai ilustrasi, GameThread biasanya menggunakan proporsi terbesar dari
waktu CPU, terutama karena Tick Time. Selain itu, sebagian besar Tick Time digunakan oleh tugas yang terkait dengan
FActorComponentTickFunction.
Untuk mengoptimalkan FActorComponentTick, Anda harus mengecualikan penghitungan dan menerapkan penghapusan untuk karakter dan objek yang diposisikan di luar bidang pandang kamera. Selain itu, memanfaatkan animasi berbasis LOD (Level of Detail)
dapat menghasilkan peningkatan performa lebih lanjut.
Unity Profiler (Unity)
Analisis menggunakan Unity Profiler mengungkapkan bahwa Thread Utama menggunakan lebih dari 45 md, dengan PostLateUpdate.FinishFrameRendering menggunakan 16,23 md, sehingga menjadikannya operasi yang paling memakan waktu. Dalam hal ini, beberapa pemanggilan Inl_RenderCameraStack diamati. Sebaiknya pastikan kebutuhan kamera yang diaktifkan dan optimalkan kamera tersebut dengan tepat.
Alat pembuatan profil tingkat sistem
Gunakan alat pembuatan profil berikut:
Perfetto
Dengan menggunakan rekaman aktivitas Perfetto, Anda dapat menentukan penetapan core CPU dan detail eksekusi setiap thread di perangkat yang didukung Android. Hal ini memungkinkan Anda mengidentifikasi hambatan performa dengan menganalisis data eksekusi thread.
Kasus overhead CPU
Rekaman aktivitas menunjukkan bahwa beban kerja di GameThread dan RenderThread menyebabkan penundaan di QueuePresent RHI Thread, sehingga menyebabkan skenario terikat CPU, berdasarkan VSync.
Kasus overhead GPU
Rekaman aktivitas menunjukkan bahwa penyelesaian GPU itu sendiri melebihi 25 md, yang menandakan skenario terikat GPU.
Simpleperf
Untuk mengidentifikasi fungsi dengan penggunaan CPU saat ini tertinggi, simpleperf dapat digunakan. Untuk hasil yang optimal, sebaiknya urutkan fungsi ini untuk memprioritaskan dan mengatasi fungsi dengan penggunaan tertinggi terlebih dahulu.
Simpleperf membantu Anda memeriksa data tentang fungsi yang menggunakan waktu CPU paling banyak. Untuk mengoptimalkan penggunaan CPU, mulailah dengan fungsi yang paling banyak menggunakan CPU. Dalam
contoh ini, USkeletalMeshComponent, yang terkait dengan animasi di
ActorComponentTickFunctions, menggunakan CPU paling banyak.
Pengoptimalan GPU
Jika analisis menunjukkan bahwa game terikat GPU, penyelidikan lebih lanjut sangat penting. Hal ini memerlukan penggunaan berbagai alat dan teknik untuk pengoptimalan dan analisis GPU.
Untuk mengoptimalkan GPU, gunakan debugger frame untuk menganalisis pipeline render dan panggilan gambar untuk setiap adegan. Selain itu, Anda harus memahami arsitektur GPU dan perilaku pipeline secara menyeluruh untuk mengidentifikasi operasi yang tidak perlu atau area yang perlu dioptimalkan.
Bagian berikut menjelaskan metode dan alat untuk pengoptimalan GPU.
Menghilangkan RenderPass yang tidak perlu
Untuk meningkatkan performa rendering dan mengurangi beban kerja GPU, hilangkan proses rendering yang tidak perlu. Hal ini mencakup semua render pass yang tidak memiliki panggilan gambar atau yang outputnya tidak digunakan dalam frame akhir.
Gunakan debugger GPU, seperti RenderDoc, untuk menganalisis pipeline rendering dan
mengidentifikasi peluang pengoptimalan.
Tidak Ada Panggilan Draw: Periksa apakah render pass menyertakan panggilan draw. Jika tidak ada panggilan gambar, hapus kartu.
Output yang Tidak Digunakan: Periksa apakah proses berikutnya mengakses atau menampilkan output proses rendering, misalnya, warna atau kedalaman. Jika tidak, hapus kartu tersebut.
Kartu yang Dapat Digabungkan: Identifikasi kartu yang dapat Anda gabungkan:
- Framebuffer atau lampiran yang sama
- Operasi muat atau penyimpanan yang kompatibel
- Tidak ada penghalang dependensi di antaranya
Meminimalkan operasi pemuatan atau penyimpanan
Operasi pemuatan atau penyimpanan sangat menguras resource karena menggunakan banyak memori.
Minimalkan operasi load-store yang tidak perlu. Lakukan tindakan ini hanya jika lampiran dalam RenderPass diperlukan. Jika tidak, ganti dengan operasi
Clear atau Don't care untuk mengurangi overhead.
Cara mengoptimalkan
Gunakan debugger GPU, seperti RenderDoc, untuk menganalisis pipeline rendering dan
mengidentifikasi peluang pengoptimalan berikut:
Pemuatan: Jika lampiran render pass tidak menggunakan data dari pass atau lampiran sebelumnya, operasi pemuatan tidak diperlukan. Dalam kasus seperti ini, penggunaan
Don't careatauCleardapat mengurangi overhead.Store: Jika lampiran render pass tidak digunakan setelah render pass saat ini, operasi penyimpanan tidak diperlukan. Dalam kasus seperti itu, gunakan
Don't careatauClear.Ganti: Tentukan apakah setelan pemuatan atau penyimpanan saat ini dapat diganti dengan
ClearatauDon't Caretanpa memengaruhi frame akhir.
Menghindari penghapusan untuk mengaktifkan Early-Z
Early-Z meningkatkan performa di platform seluler. Namun, instruksi discard
dalam shader akan otomatis menonaktifkan Early-Z. Jika petunjuk discard
tidak penting, hapus petunjuk tersebut.
Akselerasi Early-Z
Pengoptimalan ini secara signifikan mengurangi operasi shader fragmen dan meningkatkan performa GPU.
Early-Z Pengujian kedalaman dan stensil
Cara mengoptimalkan
Gunakan debugger GPU, seperti RenderDoc, untuk menganalisis pipeline rendering dan
mengidentifikasi peluang pengoptimalan berikut:
Penggunaan
discarddalam shader fragmen: Kata kuncidiscardmencegah GPU melakukan pengujian kedalaman awal karena visibilitas fragmen tidak diketahui sebelumnya.Modifikasi
gl_FragDepth: Memodifikasigl_FragDepthsecara dinamis mengubah kedalaman fragmen, yang menonaktifkan pengoptimalan Z Awal karena kedalaman akhir tidak diketahui sebelum pemrosesan fragmen.Diaktifkan dari alfa ke cakupan: Jika diaktifkan dari alfa ke cakupan (sering digunakan dalam rendering MSAA), cakupan fragmen bergantung pada nilai alfa. Hal ini dapat menunda pengujian kedalaman dan menonaktifkan Early-Z.
Mengoptimalkan format tekstur
Pemilihan format tekstur yang optimal akan mengurangi penggunaan memori, meningkatkan efisiensi bandwidth, dan meningkatkan performa rendering. Menggunakan format presisi tinggi yang berlebihan dapat membuang-buang resource GPU tanpa memberikan keuntungan visual.
Cara mengoptimalkan
Gunakan debugger GPU, seperti RenderDoc, untuk menganalisis pipeline rendering dan
mengidentifikasi peluang pengoptimalan berikut:
- Gunakan
D24S8, bukanD32S8untuk buffer stensil kedalaman: PenggunaanD24S8untuk buffer stensil kedalaman mengurangi penggunaan memori sebesar 20% dibandingkan denganD32S8, dengan sedikit atau tanpa perbedaan kualitas visual yang terlihat di sebagian besar aplikasi. - Gunakan kompresi
ASTCuntuk tekstur warna: KompresiASTCmengurangi penggunaan memori tekstur secara signifikan—hingga 8x dibandingkan dengan format yang tidak dikompresi—sekaligus mempertahankan kualitas visual yang tinggi. - Gunakan format half-float, bukan full-float: Gunakan
R16FatauRG16Funtuk mengurangi bandwidth memori dan konsumsi penyimpanan. Format ini sangat cocok untuk buffer pasca-pemrosesan.
Mengoptimalkan kompleksitas geometri
Meminimalkan kompleksitas geometris meningkatkan performa rendering, terutama di perangkat seluler dengan kemampuan GPU terbatas. Hal ini melibatkan penggunaan jumlah verteks dan segitiga yang lebih sedikit, menggabungkan objek untuk mengurangi panggilan gambar, dan menghilangkan geometri yang tidak dirender atau tidak diperlukan. Teknik seperti penyederhanaan mesh, Level Detail (LOD), dan penghapusan frustum atau oklusi dapat mengurangi beban kerja GPU secara signifikan dan meningkatkan kecepatan frame.
Cara mengoptimalkan
Gunakan alat pembuatan profil dan debugger GPU, seperti RenderDoc, Android GPU
Inspector, atau penganalisis performa lainnya, untuk mengidentifikasi bottleneck performa
terkait geometri.
Kurangi Jumlah Segitiga: Minimalkan penggunaan poligon, terutama untuk objek kecil atau jauh.
Gunakan Tingkat Detail (LOD): Berdasarkan jarak kamera, mesh yang lebih sederhana digunakan secara otomatis.
Gabungkan Mesh Kecil: Gabungkan objek statis untuk mengurangi panggilan gambar dan overhead CPU.
Frustum dan Occlusion Culling: Hindari merender objek yang berada di luar tampilan atau terhalang oleh elemen lain.
Menghapus lampiran yang tidak diperlukan
Lampiran render pass (misalnya, warna, kedalaman, stensil) menggunakan bandwidth memori dan resource GPU, meskipun tidak digunakan. Menghapus lampiran yang tidak perlu atau berlebihan akan meningkatkan performa dan mengurangi konsumsi daya, terutama di platform seluler.
Cara mengoptimalkan
Gunakan alat pembuatan profil dan debugger GPU, seperti RenderDoc,
Android GPU Inspector, atau penganalisis performa lainnya, untuk mengidentifikasi
bottleneck performa terkait geometri.
- Periksa penggunaan sebenarnya: Apakah ada panggilan gambar atau shader yang menulis ke atau membaca dari lampiran?
- Analisis output frame: Gunakan
RenderDocatau utilitas serupa untuk menentukan apakah lampiran berkontribusi pada gambar akhir. - Pertimbangkan lampiran sementara atau dummy: Lampiran sementara atau operasi penyimpanan 'Tidak Penting' harus digunakan untuk data sementara yang tidak memerlukan penyimpanan persisten.
Mengoptimalkan presisi shader
Menggunakan presisi yang terlalu tinggi (misalnya, highp, bukan mediump atau lowp) dalam shader akan meningkatkan beban kerja GPU, konsumsi daya, dan tekanan register, terutama pada GPU seluler. Dengan menggunakan presisi yang memadai dan terendah untuk variabel (misalnya, posisi, warna, UV), Anda dapat meningkatkan performa tanpa dampak visual yang terlihat.
Cara mengoptimalkan
Gunakan alat pembuatan profil dan debugger GPU seperti RenderDoc, Android GPU Inspector, atau penganalisis performa lainnya untuk mengidentifikasi bottleneck performa terkait geometri.
Tinjau kode shader: Nilai variabel shader dan konfirmasi bahwa presisi tinggi hanya digunakan jika diperlukan, seperti untuk komputasi ruang layar atau kedalaman. Gunakan presisi sedang atau rendah untuk warna, koordinat UV, atau nilai yang tidak memerlukan presisi tinggi.
Gunakan debugger GPU: Utilitas diagnostik, seperti RenderDoc atau profiler GPU seluler (misalnya, AGI, Mali/GPU Inspector), mengidentifikasi penggunaan register yang tinggi atau shader yang terhenti terkait dengan masalah presisi.
Mengaktifkan penghapusan sisi belakang
Merender segitiga yang menghadap menjauhi kamera (sisi belakang) sering kali tidak diperlukan untuk objek solid.
Cara mengoptimalkan
Penggunaan VK_CULL_MODE_NONE dapat memengaruhi performa secara negatif karena memaksa GPU merender sisi depan dan belakang, yang meningkatkan beban kerja rendering.
Meminimalkan overdraw dalam adegan UI
Hilangkan panggilan gambar dan proses rendering yang tidak perlu, terutama di adegan UI, untuk meningkatkan performa rendering dan mengurangi beban kerja GPU. Misalnya, dalam adegan UI di mana seluruh dunia dirender sebelum melapisi UI di seluruh layar, merender dunia menjadi berlebihan.
Cara mengoptimalkan
Gunakan debugger GPU, seperti RenderDoc, untuk menganalisis pipeline rendering dan
mengidentifikasi peluang pengoptimalan berikut:
- Verifikasi tidak adanya overdraw yang berlebihan. Dalam konteks antarmuka pengguna, tempat seluruh layar mungkin dirender, pastikan bahwa proses rendering sebelumnya tidak digambar berlebihan tanpa alasan.
- Aktifkan pengujian dan seleksi kedalaman untuk mengoptimalkan performa.
- Pertimbangkan urutan rendering dari depan ke belakang.