Mengoptimalkan Kode Rendering Sisi CPU
Keseimbangan Beban Komputasi CPU dan GPU
Optimalisasi kode rendering Godot pada sisi CPU berfokus pada pengurangan kerja yang tidak perlu karena kinerja akhir selalu ditentukan oleh prosesor yang lebih lambat di antara CPU dan GPU.
Ketika menulis renderer, kompromi terus dibuat untuk menguntungkan salah satu prosesor karena tidak ada jumlah optimalisasi CPU yang dapat menyelamatkan shader yang tidak efisien. Batching pada 2D adalah teknik yang sedikit merugikan kinerja GPU tetapi sangat meningkatkan kinerja CPU, dan karena gim 2D cenderung mengalami hambatan CPU sebelum mengalami hambatan GPU maka batching menjadi keuntungan bersih dalam sebagian besar kasus. Sebaliknya gim 3D cenderung lebih sering mengalami hambatan GPU sehingga Godot melakukan occlusion culling pada CPU untuk mengurangi beban GPU. Terlepas dari keseimbangan akhir antara kerja CPU dan GPU, optimalisasi mesin selalu bermanfaat karena setiap optimalisasi pada mesin menyisakan lebih banyak ruang bagi pengembang gim untuk menggunakan CPU dan GPU serta menghemat daya baterai pada platform yang dibatasi daya.
Siklus dan Tantangan Profiling Kinerja
Proses optimalisasi kinerja CPU secara garis besar terdiri atas mengidentifikasi kemacetan atau hotspot kinerja, memahami mengapa kemacetan berada di lokasi tersebut, menyelidiki solusi, mengukur ulang kinerja, lalu mengulanginya. Tahap identifikasi merupakan bagian tersulit karena ketika gim atau aplikasi mulai berjalan buruk maka sulit melacak penyebab pastinya, padahal sering kali setelah lokasi kinerja buruk terlihat maka penyebabnya langsung dipahami, misalnya pemanggilan API yang mahal secara tidak sengaja di dalam loop yang panas atau kode debug yang tertinggal dan seharusnya tidak pernah muncul. Bagi pengembang mesin tantangannya bertambah karena harus mengoptimalkan untuk rentang gim potensial yang sangat luas tanpa satu target tunggal melainkan ribuan target yang bergerak, dan masalah serius sering baru diketahui ketika ada laporan bug, meskipun banyak perbaikan dapat diidentifikasi hanya dengan menguji demo yang ada dan gim sumber terbuka.
Alat terbaik untuk mengidentifikasi kemacetan CPU adalah profiler CPU yang tersedia dalam banyak bentuk dan ukuran. Godot bahkan memiliki dua profiler bawaan di dalam editor, yaitu satu untuk memprofilkan kode GDScript dan satu lagi khusus untuk memprofilkan renderer, namun ketika mengerjakan kode mesin itu sendiri maka diperlukan profiler eksternal. Godot mendukung pembangunan dengan Tracy untuk melakukan profiling penelusuran, tetapi profiler pengambilan sampel eksternal juga dapat digunakan. Alat pilihan tersebut adalah Superluminal yang merupakan profiler pengambilan sampel eksternal. Superluminal menempel pada instans Godot yang sedang berjalan dan merekam data profiling selama mesin dijalankan, lalu menyediakan visualisasi yang sangat baik tentang kode apa yang berjalan pada waktu tertentu. Superluminal juga baru mendapatkan dukungan Linux dan contoh-contoh pembahasan ini menggunakan Superluminal.
Terkadang kemacetan mudah dipahami, terutama ketika kinerja buruk disebabkan oleh bug dan bukan oleh pilihan desain yang buruk, meskipun sering kali pemahaman kemacetan membutuhkan pemahaman kode di sekitarnya. Pada akhirnya optimalisasi kinerja bermuara pada prinsip tidak melakukan hal-hal yang tidak perlu dilakukan, sehingga pemahaman kode dan pemahaman sistem sangat membantu untuk memahami hal apa yang perlu dilakukan dan hal apa yang tidak perlu. Biasanya setelah kemacetan dipahami maka solusinya akan tampak jelas, tetapi terkadang perlu mencoba beberapa hal berbeda sebelum mengetahui secara tepat apa yang dibutuhkan. Sebagai contoh, loop yang berat dengan banyak perhitungan di dalamnya pada awalnya dapat dikira perlu dikurangi perhitungannya per iterasi, padahal masalahnya dapat berasal dari bandwidth memori sehingga penataan ulang data dapat menghasilkan peningkatan kinerja yang lebih baik. Pada akhirnya perubahan yang dibuat secara langsung mengurangi dampak kemacetan tersebut.
Pengukuran ulang selalu diperlukan karena komputer sangat kompleks dan mesin gim sangat kompleks, sehingga hal yang dikira akan lebih cepat terkadang ternyata tidak lebih cepat. Optimalisasi sering berlawanan dengan intuisi. Sebagai contoh, penambahan cabang di dalam loop for untuk melewatkan sebagian perhitungan secara intuitif tampak akan selalu lebih cepat, tetapi dalam banyak kasus hal itu menghentikan kompiler melakukan vektorisasi loop sehingga menghasilkan kinerja yang lebih buruk. Contoh lain yang lebih umum adalah menyimpan nilai dalam cache untuk menghindari perhitungan ganda, tetapi jika hambatan pada jalur kode tersebut adalah pembacaan memori maka penyimpanan cache akan membuat kode lebih lambat dan akan lebih baik melakukan pekerjaan itu dua kali. Kecuali merupakan ahli kompiler C++ yang mutlak, hasil pasti perubahan kode terhadap kinerja kemungkinan tidak dapat ditebak, sehingga selalu lebih baik mengukur ulang dan mengonfirmasi hasilnya.
Studi Kasus Optimasi Animasi 2D
Kasus pertama muncul dari percakapan dengan Aurélien Condomines yang mengerjakan Heidi’s Legacy: Mountains Calling. Aurélien mencatat bahwa kode animasi di Godot sangat lambat sehingga membatasi kemampuannya menambah konten ke dalam gimnya. Ia menemukan bahwa dengan sekitar 20 karakter animasi maka kinerja mulai menurun pada gimnya. Angka tersebut mengejutkan karena kode animasi seharusnya tidak menjadi hambatan hanya dengan sekitar 20 karakter animasi sehingga penyelidikan diputuskan. Ia diminta menyusun proyek minimal dengan banyak sprite animasi di layar agar dapat diprofilkan. Petunjuk pertama adalah ia menggunakan AnimationPlayer untuk menganimasikan verteks Polygon2D yang ditampilkan pada Viewport pada Sprite3D agar memperoleh animasi 2D yang tampak bagus di dunia 3D. Pendekatan itu saja terdengar unik, tetapi bukan sesuatu yang seharusnya menimbulkan masalah signifikan, sehingga langkah berikutnya adalah menjalankan Superluminal dan melihat apa yang terjadi.
Pengambilan beberapa detik demo yang berjalan dilakukan lalu diperbesar pada satu bingkai. Semua pengujian adegan demo ini dilakukan dengan CPU Ryzen 5 9600X. Karena tidak sepenuhnya jelas dalam tangkapan layar, rincian penggunaan waktunya adalah 4,6 ms untuk AnimationMixer yang lebih lambat daripada yang diinginkan, tetapi terdapat banyak track animasi pada adegan ini karena setiap verteks dianimasikan sehingga tidak sepenuhnya di luar dugaan. Sebanyak 15,7 ms digunakan untuk Polygon2D::_notification(). Sebanyak 11,7 ms digunakan untuk menggambar adegan, dengan rincian 3,3 ms dihabiskan untuk membuat vertex array dan 5 ms dihabiskan untuk membebaskan vertex array, sehingga hanya sekitar 3,4 ms yang benar-benar digunakan untuk merender adegan. Dengan gambaran tingkat tinggi tersebut, dua hal yang salah dapat langsung diketahui, yaitu Polygon2D melakukan sesuatu yang tidak terduga dan apa pun itu mengakibatkan vertex array dibuat dan dibebaskan setiap bingkai.
Untuk melihat lebih dekat ke mana waktu dihabiskan di Polygon2D, terdapat dua pilihan yaitu memperbesar garis waktu atau menggunakan call graph di bawah untuk memperoleh gambaran lebih rinci. Call graph mengurutkan pemanggilan fungsi di dalam fungsi yang dipilih berdasarkan biayanya. Terlihat bahwa sebagian besar waktu dihabiskan untuk menambahkan surface baru dan membebaskan surface sebelumnya. Hal itu selaras dengan apa yang sudah terlihat pada kode rendering, sehingga kini diketahui bahwa kelas Polygon2D bertanggung jawab atas kinerja buruk bahkan pada waktu rendering. Panel Source and Disassembly kemudian dapat dilihat untuk mengetahui baris kode mana yang bertanggung jawab. Kode tersebut bertanggung jawab membangun mesh internal untuk Polygon2D. Profil tersebut menunjukkan bahwa kode itu berjalan setiap bingkai dan sangat lambat. Setelah semuanya dirangkai, tidak mengejutkan bahwa mesh dibangun ulang setiap bingkai karena verteks berubah pada CPU setiap bingkai.
Pembaruan Data Mesh secara Langsung
Berdasarkan hasil profil tersebut, proses itu diketahui sebagai proses yang sangat lambat sehingga pertanyaannya adalah apakah sesuatu dapat dilakukan. Godot mengekspos API tingkat rendah untuk memperbarui data verteks secara manual. Hal ini dapat sangat membantu ketika jumlah verteks dalam mesh diketahui akan tetap sama. Alih-alih membebaskan mesh lama, mengalokasikan mesh baru, lalu mengunggah data verteks ke mesh baru, data verteks dapat langsung diunggah ke mesh yang sudah ada. Itulah yang dilakukan dalam pull request tersebut. Sebagian besar pekerjaan dalam PR adalah mengidentifikasi kasus mana yang cocok untuk pembaruan mesh dan kasus mana yang membutuhkan pembuatan ulang mesh. Optimalisasi tersebut kira-kira melipatgandakan tiga kali kinerja Polygon2D yang dianimasikan sehingga teknik yang digunakan Aurélien menjadi sepenuhnya layak.
Melihat satu bingkai setelah PR tersebut, waktu turun dari 35 ms per bingkai menjadi hanya 13 ms. Rendering kini mencakup sekitar 50% waktu bingkai, animasi sekitar 30%, dan pembaruan Polygon2D sekitar 20%. Bagian kode yang sama tentu dapat dioptimalkan jauh lebih lanjut. Salah satunya adalah pengunggahan ulang seluruh data mesh setiap bingkai ketika verteks mana pun bergerak. Optimalisasi potensial adalah hanya memperbarui wilayah mesh yang berubah, sehingga jika animasi hanya memengaruhi sedikit verteks maka biaya pengunggahan penuh tidak perlu dibayar. Namun optimalisasi semacam itu perlu dipandu oleh gim atau adegan yang benar-benar diuntungkan oleh pembaruan parsial. Gim ini pada dasarnya menganimasikan setiap verteks setiap bingkai sehingga pelacakan yang dibutuhkan untuk pembaruan parsial kemungkinan menjadi kerugian bersih. Perlu diingat bahwa untuk mengoptimalkan sesuatu dengan benar maka optimalisasi perlu dapat diuji. Tanpa kasus uji, optimalisasi tidak dapat dilakukan dengan benar. Pada akhirnya optimalisasi kecil dan aman tersebut menghasilkan peningkatan yang cukup untuk naik dari 28 FPS menjadi 83 FPS pada perangkat tersebut, sehingga cukup untuk saat ini. Jika seseorang benar-benar tertarik dan memiliki waktu, kinerja dapat didorong lebih jauh, meskipun yang paling menarik kini adalah ke mana waktu dihabiskan dalam bingkai rendering karena mencakup 50% waktu bingkai.
Penyelidikan Kinerja Shader Backend D3D12
Pada awal tahun ini, Asilkan menyelidiki pipa kompilasi shader pada backend D3D12. Metode saat ini untuk mengompilasi shader dengan D3D12 dijelaskan dalam tulisan blog Pedro sebelumnya. Singkatnya tahapannya adalah mengompilasi GDShader menjadi GLSL menggunakan kompiler shader bawaan, mengompilasi GLSL menjadi SPIRV menggunakan GLSLang, mentranspilasi SPIRV menjadi DXIL menggunakan konverter NIR milik Mesa, lalu menyerahkan DXIL ke driver GPU untuk dikompilasi menjadi biner. Sejauh ini bagian paling lambat dari proses tersebut adalah tahap transpilasi. Selain itu, hal itu menimbulkan perbedaan waktu pemuatan yang kentara ketika beralih dari Vulkan ke DXIL karena Vulkan dapat menggunakan SPIRV secara langsung. Untuk mengoptimalkannya, Asilkan menjalankan trace melalui Superluminal dan menganalisis hasilnya. Dari tingkat yang sangat tinggi sudah terlihat kesenjangan signifikan ketika hanya beberapa thread yang aktif. Kesenjangan dalam eksekusi thread berarti berbagai hal memakan waktu lebih lama daripada yang seharusnya secara teoretis. Idealnya proses pemuatan akan memenuhi semua inti sepenuhnya agar dimuat secepat mungkin dan itulah tujuannya.
Sejak awal terlihat sesuatu yang salah. Warna hijau berarti thread sedang melakukan pekerjaan dan tinggi batang hijau pada dasarnya menunjukkan seberapa keras thread bekerja, sedangkan warna merah berarti thread terhenti menunggu sesuatu. Keindahan profiler seperti Superluminal dalam situasi seperti ini adalah kemampuan memperbesar tampilan dan melihat apa yang terjadi. Di sini Asilkan dapat melihat bahwa kesenjangan tersebut semuanya disebabkan oleh thread yang meminta lebih banyak memori dari OS. Meskipun pekerjaan sangat multithread, semua thread akhirnya saling menunggu sementara menunggu OS mengalokasikan lebih banyak memori heap. Pertanyaannya adalah bagaimana jika hal itu tidak dilakukan. Yang akhirnya dilakukan Asilkan adalah memberi setiap thread heap miliknya sendiri dan memintanya mengalokasikan dari heap tersebut alih-alih sering meminta lebih banyak memori kepada OS dari satu heap global. Hasil akhirnya adalah thread tidak lagi terhenti menunggu OS dan eksekusinya kini tampak padat. Optimalisasi tersebut menghemat 11 detik waktu pemuatan pada demo TPS. Sebelas detik tersebut murni waktu terbuang ketika CPU terhenti tanpa melakukan apa pun.
Clay John berharap uraian tersebut menunjukkan apa yang sebenarnya dilakukan untuk mengoptimalkan komponen renderer karena prosesnya tidak terlalu rumit, terutama ketika alat yang tepat tersedia, dan dalam kedua kasus solusinya sangat jelas setelah masalah diidentifikasi dengan benar. Godot adalah mesin gim sumber terbuka nirlaba yang dikembangkan oleh ratusan kontributor pada waktu luang serta segelintir pengembang paruh waktu atau penuh waktu yang direkrut berkat donasi murah hati dari komunitas Godot, dengan terima kasih besar kepada semua pihak yang telah menyumbangkan waktu atau dukungan finansialnya. Dukungan finansial untuk membantu mengamankan perekrutan di masa depan dapat dilakukan menggunakan platform Godot Development Fund yang dikelola oleh Godot Foundation, dan terdapat juga beberapa cara donasi alternatif yang dapat lebih sesuai.
Terjemahan dan adaptasi Bahasa Indonesia menggunakan AI oleh Zennovan News. Sumber bahan asli: Optimizing CPU-side Rendering Code. Clay John / Godot Engine. Lisensi prosa sumber: MIT. Ketentuan sumber. Adaptasi ini tidak menyiratkan dukungan penerbit asli.
Copyright (c) 2017-present Godot Engine contributors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Komentar (0)
Masuk dengan Google untuk menulis komentar. Komentar ditinjau sebelum tampil.
Masuk dengan GoogleBelum ada komentar. Jadilah yang pertama!
Berita Terkait
Godot 4.8 Beta 2 Masuki Tahap Pembersihan Regresi dan Pembaruan Fondasi .NET
Tim pengembang merilis snapshot Godot 4.8 beta 2 dua pekan setelah versi beta pertama, dengan fokus menuntaskan hampir 150 kasus regresi pada fase yang…

Godot Engine Hadirkan Editor Mandiri dan Dukungan Wadah Spasial untuk XREAL AURA
Peluncuran kacamata XREAL AURA menandai kehadiran perangkat optical see-through pertama yang beroperasi dengan sistem Android XR, dengan Godot Engine…

Peretasan Peladen ContentDB Picu Penonaktifan Repositori dan Imbauan Reset Kredensial
Pengelola ekosistem Luanti memutuskan untuk menonaktifkan layanan ContentDB secara penuh menyusul insiden penyusupan keamanan yang berlangsung antara 22…

Penyelenggaraan Godot XR Community Game Jam VI Mengusung Tema Adrift
Ajang Godot XR Community Game Jam VI resmi berlangsung mulai tanggal 21 September 2026 hingga 5 Oktober 2026 dengan menghadirkan tema bertajuk Adrift.