Optimizacija CPU renderinga u Godotu: kako je profiliranje pretvorilo dva uska grla u ozbiljan dobitak
Public- 15 Sep, 2026

Optimizacija renderinga izvana često izgleda kao crna magija, no zapravo je proces daleko metodičniji nego što se čini. Programeri Godota detaljno su ispričali kako optimiziraju CPU kod u rendereru playa, na konkretnim primjerima iz protekle godine. Čitav rad počiva na jednom ključnom principu: svaki engine mora balansirati opterećenje između CPU-a i GPU-a, jer konačnu izvedbu uvijek određuje sporiji od njih dvojice. Nikakvo podešavanje CPU-a ne može nadoknaditi neučinkovite shadere. Tim playa stalno pristaje na takve kompromise — primjerice, 2D batching lagano pogoršava GPU izvedbu, ali znatno poboljšava CPU izvedbu, a budući da 2D igre obično prve udare u CPU, batching gotovo uvijek donosi čistu dobit. 3D igre su, naprotiv, najčešće ograničene GPU-om, pa Godot izvodi occlusion culling na CPU-u kako bi rasteretio GPU. Uz to, svaka optimizacija štedi bateriju na platformama s ograničenim napajanjem.
Osnovni ciklus optimizacije CPU-a je jednostavan: pronaći usko grlo, razumjeti kako sustav radi, unijeti promjenu i ponovno izmjeriti. Ponovno mjerenje je važno jer se optimizacije često ponašaju neočekivano. Dodati grananje unutar petlje kako bi se preskočio suvišan posao čini se bržim, ali ono može blokirati vektorizaciju kompilatora i sve usporiti. Isto tako, keširanje vrijednosti radi izbjegavanja suvišnih izračuna može dati suprotan učinak ako je pravo usko grlo čitanje iz memorije: u tom bi slučaju obaviti posao dvaput bilo brže. Pronaći krivca najteži je korak, a počinje od profajlera. U Godotu su ugrađena dva: jedan za GDScript, drugi za renderer. No za sam kod playa programerima treba vanjski alat. U članku se koristi Superluminal — profajler s uzorkovanjem koji se spaja na pokrenutu instancu Godota, bilježi podatke kako engine radi, prikazuje koji se kod kada izvršava i nedavno je dobio podršku za Linux. Godot također podržava izgradnju s Tracyjem za profiliranje na temelju traceanja.
Prvi primjer nastao je iz razgovora s programerom igre Heidi's Legacy: Mountains Calling, koji je otkrio da je kod animacije u Godotu toliko spor da je sadržaj morao biti ograničen na otprilike dvadeset animiranih likova. Postavka je uključivala AnimationPlayer koji animira vrhove Polygon2D prikazanog na Viewportu povrh Sprite3D — tehnika neobična, ali sama po sebi nije skupa. Profiliranje minimalne reprodukcije (testirano na Ryzen 5 9600X) odmah je otkrilo krivca. Klasa Polygon2D gotovo je sve vrijeme trošila na to da svaki kadar oslobađa prethodnu površinu mesha i dodjeljuje novu, jer se podaci vrhova na CPU-u mijenjaju svaki kadar. Graf poziva i kartica «Source and Disassembly» ukazali su na točne retke koji ponovno grade unutarnji mesh, potvrdivši nepotrebnu buku.
Rješenje je iskoristilo niskorazinski API Godota za ručno ažuriranje podataka vrhova na mjestu. Kad se broj vrhova ne mijenja, nije potrebno oslobađati i ponovno dodjeljivati mesh — dovoljno je učitati nove podatke vrhova u postojeći mesh. Najveći dio pull requesta sastojao se u tome da se odluči u kojim je slučajevima sigurno ažurirati na mjestu, a u kojima je zaista potrebno ponovno stvaranje. Na kraju su performanse animiranih Polygon2D porasle otprilike triput: vrijeme kadra smanjilo se s 35 ms na 13 ms, a scena je porasla s 28 FPS na 83 FPS. Nakon promjene na renderiranje je otpadalo oko 50% vremena kadra, na animaciju oko 30%, a na ažuriranje Polygon2D 20%. Autor je istaknuo da je moguć i daljnji dobitak — ponovno učitavanje samo promijenjenih područja mesha umjesto cijelog mesha — no takva djelomična ažuriranja zahtijevaju stvarni testni scenarij koji bi ih vodio, a budući da ova igra animira gotovo svaki vrh u svakom kadru, praćenje djelomičnih promjena najvjerojatnije bi donijelo čist minus.
Drugi slučaj odnosio se na cjevovod kompilacije shadera D3D12 u Godotu, koji je preuzet u rad nakon pritužbi na sporo učitavanje. Najsporiji korak cjevovoda bila je transpilacija, a prelazak s Vulkana na DXIL donosio je primjetnu kaznu na vrijeme učitavanja, jer Vulkan može izravno trošiti SPIR-V. Praćenje u Superluminalu pokazalo je uzrok: gusta višenitnost s velikim zastojima u kojima su aktivne tek dvije niti. Zumiranje je pokazalo da su zastoji nastajali kad su sve niti istovremeno od OS-a tražile više hrpe memorije — čekale su na dodjelu iz jedinstvene globalne hrpe. Rješenje je svakoj niti dalo vlastitu hrpu umjesto stalnih pozivanja zajedničke globalne hrpe, uklonivši zastoje. To je uštedjelo 11 sekundi vremena učitavanja u TPS demou — vrijeme tijekom kojeg je CPU potpuno mirovao. Oba primjera potvrđuju jednu lekciju: s pravim alatom rješenje postaje očito čim je problem ispravno definiran.