Poglavlje 39: Optimizacija -- Filozofija i Pristup

Poglavlje 39: Optimizacija -- Filozofija i Pristup

Deo 6: Optimizacija -- Majstorski Kurs

"Premature optimization is the root of all evil." -- Donald Knuth, 1974.

Ali takodje: "We should not pass up our opportunities in that critical 3%." -- isti taj Donald Knuth, ista ta recenica, deo koji svi zaboravljaju da citiraju.


Dobrodosli u sesti deo knjige. Sve sto ste ucili do sada -- rendering pipeline, GPU arhitektura, shaderi, materijali, Nanite, Lumen, Virtual Shadow Maps, Niagara, TSR -- sve to je bilo gradivni materijal. Sada ucimo kako da taj materijal ucinimo brzim.

Ali pre nego sto otvorite profajler, pre nego sto pocnete da smanjujete rezoluciju tekstura i brisete poligone, morate da razumete jednu fundamentalnu stvar: optimizacija nije nasumicno smanjivanje stvari dok igra ne prodje brze. Optimizacija je disciplina. Ima svoju metodologiju, svoja pravila, svoje zamke. I ako je radite pogresno, potrosicete nedeljama rada da poboljsate performanse za 0.3 milisekunde, dok pravi problem sedi negde potpuno drugde i kosta vas 8 milisekundi po frame-u.

Ovo poglavlje ne sadrzi nijednu specificnu optimizaciju. Necete nauciti kako da smanjite draw call-ove ili kako da podesavate Lumen kvalitet. To dolazi u narednim poglavljima. Ovo poglavlje je filozofsko -- ali to ne znaci da je manje vazno. Naprotiv. Ovo je najvaznije poglavlje u celom delu o optimizaciji, jer bez pravilnog pristupa, sve specificne tehnike koje cete uciti su kao posedovanje kutije sa alatom ali bez znanja sta treba popraviti.

Hajde da krenemo.


39.1 Kardinalno Pravilo: Ne Optimizuj Naslepo

Zasto ovo mora biti prvo

Zamislite sledeci scenario. Radite na igri u Unreal Engine 5. Igra radi na 25 FPS na vasem ciljnom hardveru. Otvorite scenu, pogledate je, i pomislite: "Pa naravno, imam previse trouglova. Nanite ne moze da izadje na kraj sa ovim. Moram da smanjim geometriju."

Provedete dve nedelje optimizujuci mesh-eve. Smanjite polycount za 40%. Impresivno. Otvorite igru ponovo. 26 FPS.

Jedan frame per second poboljsanja. Za dve nedelje rada.

Zasto? Zato sto problem nikada nisu bili trouglovi. Problem su bile senke -- Virtual Shadow Maps su generisale ogromnu kolicinu stranica jer ste imali stotine point light-ova bez pravilnog podesavanja. GPU je trosio 12 milisekundi samo na senke. A vi ste trosili vreme na geometriju koja je kostala 1.5 milisekundi.

Ovo nije hipoteticki primer. Ovo se desava konstantno u game development-u. Ljudi optimizuju ono sto misle da je problem, umesto ono sto zaista jeste problem.

"Premature optimization is the root of all evil"

Ovu recenicu je napisao Donald Knuth 1974. godine u svom radu "Structured Programming with go to Statements". Postala je jedna od najcitiranijih izjava u programiranju. I jedna od najcesce pogresno protumacenih.

Ono sto Knuth zapravo kaze nije "nikad ne optimizuj". Pun citat glasi:

"Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%."

Kljucna rec je premature -- preuranjena. Problem nije optimizacija sama po sebi. Problem je optimizacija pre nego sto znate sta treba optimizovati. Problem je optimizacija zasnovana na pretpostavkama umesto na merenjima.

Prakticne posledice

Sta ovo znaci za vas, konkretno, kada radite u Unreal Engine 5?

  1. Nikad ne pretpostavljajte gde je bottleneck. Vas instinkt je skoro sigurno pogresan. Cak i iskusni developeri pogresno pogadjaju bottleneck u vise od 50% slucajeva.

  2. Uvek merite pre nego sto menjate. Pre svake optimizacije -- otvorite profajler. Zapisite brojke. Tek onda menjajte.

  3. Uvek merite posle svake promene. Da li je pomoglo? Koliko? Da li je mozda pogorasalo nesto drugo?

  4. Ne optimizujte "za svaki slucaj". Ako nesto radi dovoljno brzo -- ostavite ga. Vreme koje biste potrosili na optimizaciju mozete potrositi na pravljenje boljeg sadrzaja.

  5. Optimizujte samo ono sto je potrebno. Ako vam igra treba da radi na 60 FPS i radi na 58 FPS, ne treba vam masivna optimizacija -- treba vam fino podesavanje. Ako radi na 30 FPS, trebaju vam velike, strukturalne promene.


39.2 Profiling-First Pristup: Naucni Metod Optimizacije

Ako ne mozes da ga izmeris, ne mozes da ga poboljsas

Ova izjava, cesto pripisivana fizicaru Lordu Kelvinu (mada je originalni citat malo drugaciji), savrseno opisuje sustvo optimizacije. Performanse koje ne merite su performanse o kojima nemate pojma.

"Osecaj" da igra radi sporo nije dovoljno. "Izgleda mi da se frizuje" nije metrika. Potrebni su vam konkretni brojevi:

Bez ovih brojeva, optimizacija je nagadjanje. A nagadjanje je gubenje vremena.

Naucni metod primenjen na optimizaciju

Optimizacija jeste nauka, i treba je tretirati sa istom riguroznozcu:

Korak 1: Posmatranje "Igra radi na 35 FPS umesto ciljanih 60 FPS."

Korak 2: Merenje Otvorite stat unit komandu, GPU profajler, Unreal Insights. Zabelesite podatke.

Korak 3: Hipoteza Na osnovu podataka, formulisite hipotezu: "GPU je usko grlo, i unutar GPU posla, senke trose 45% ukupnog frame vremena."

Korak 4: Eksperiment Napravite promenu koja testira hipotezu: smanjite kvalitet senki, promenite svetlosne izvore, podesite shadow map rezoluciju.

Korak 5: Ponovljeno merenje Merite ponovo, na istom mestu u sceni, sa istim uslovima. Uporedite sa prethodnim merenjima.

Korak 6: Zakljucak Da li je hipoteza potvrddjena? Da li je poboljsanje onoliko koliko ste ocekivali? Ako nije -- hipoteza je bila pogresna, ili postoji drugi faktor. Vratite se na korak 2.

Korak 7: Ponavljanje Kada ste resili jedan bottleneck, profajlirajte ponovo. Sada je mozda nesto drugo postalo najskuplji deo. Ponovite ceo proces.

Anegdota: "Ocigledni" bottleneck koji to nije bio

Dozvolite mi da podelim jednu pricu koja ilustruje zasto je profajliranje toliko vazno.

Tim radi na open-world igri. Imaju ogromne landscape-ove sa kompleksnim materijalima -- 12 layera, svaki sa svojim teksturama, blend maske, displacement. Performanse su uzasne. Ceo tim je ubedjen da su landscape materijali problem. "Pa naravno, 12 layera, ko ne bi bio spor sa tim."

Jedan junior programer insistira da se uradi profajliranje pre nego sto se ista menja. Svi se naljute -- "ocekujes da potrosimo vreme na merenje kad vec znamo sta je problem?" Ali pristanu.

Profajler pokazuje sledece:

Particle sistemi. Niagara efekti za prah, insekte, cvetni polen, vatru u daljini -- svi translucent, svi sa overdraw-om, mnogi sa lighting-om, neki sa collision-om. Niko nije ni pogledao na particle sisteme jer su vizuelno suptilni -- mali, prozracni efekti u pozadini. Ali ukupno, sa overdraw-om i kompleksnim materijalima, gutali su skoro polovinu GPU vremena.

Tim bi potrosio mesec dana optimizujuci landscape materijale i dobio bi mozda 1-2 FPS poboljsanja. Umesto toga, optimizovali su particle sisteme za tri dana i dobili 15 FPS.

Lekcija: Vizuelno dominantan element scene nije nuzno najskuplji. Vizuelno suptilni elementi mogu biti masivno skupi ispod povrsine.

Anegdota: Skriveni bottleneck koji niko nije ocekivao

Druga prica. Studio radi na multiplayer igri. Testiraju sa 32 igraca na serveru. Performanse su uzasne -- 20 FPS na klijentu. Svi se fokusiraju na rendering -- smanjuju kvalitet, optimizuju shadere, dodaju LOD-ove.

Onda neko otvori stat unit i primeti nesto cudno:

Frame:     50.0 ms
Game:      42.3 ms  ← !!!
Draw:       8.1 ms
GPU:       12.4 ms

GPU zavrsi svoj posao za 12.4 milisekunde. Cak ni render thread nije problem (8.1 ms). Game thread trosi 42.3 milisekunde -- skoro cetiri puta vise nego sto bi smeo za 60 FPS.

Problem uopste nije bio rendering. Bio je gameplay kod -- Blueprint tick funkcije koje su se izvrsavale svaki frame za svakog igraca, sa skupim kalkulacijama (line trace-ovi za vidljivost, pretraga nizova za pronalazenje najblizih objekata, cast-ovanje interfejsa). Nijedna rendering optimizacija na svetu ne bi pomogla jer GPU nije bio usko grlo.

Lekcija: Uvek prvo utvrdite koji deo sistema je usko grlo pre nego sto pocnete da optimizujete bilo koji deo.

Kako pravilno profajlirati

Postoji razlika izmedju "baciti pogled na FPS counter" i pravilnog profajliranja. Evo minimalnog pristupa:

1. Koristite konzistentne uslove testiranja

Uvek profajlirajte na istom mestu u sceni, sa istom kamerom, u istim uslovima. Ako jednom profajlirate gledajuci u prazno nebo a drugi put gledajuci u najslozeni deo scene -- rezultati nisu uporedivi.

Napravite "profiling path" -- fiksiranu putanju kamere kroz scenu (Matinee/Sequencer) koja prolazi kroz najzahtevnije delove. Pokrecete je svaki put kad profajlirate. Rezultati su ponovljivi i uporedivi.

2. Profajlirajte na ciljnom hardveru

Ako vasa ciljna platforma je PlayStation 5, profajliranje na RTX 4090 vam daje pogresne rezultate. Ne samo sto su apsolutni brojevi razliciti -- relativni odnos izmedju razlicitih sistema moze biti potpuno drugaciji. Nesto sto je jeftino na PC-ju moze biti skupo na konzoli, i obrnuto.

Ako nemate pristup ciljnom hardveru, barem profajlirajte na najslicnijem sto imate, i koristite Unreal-ov Scalability system da emulirate priblizne uslove.

3. Iskljucite promenljive

Kad profajlirate, iskljucite sve sto unosi varijabilnost:

4. Koristite prosecne vrednosti, ne pojedinacne frame-ove

Jedan frame moze da bude spor iz hiljadu razloga -- garbage collection, shader kompilacija, streaming. Merite prosek preko 100+ frame-ova. Unreal Insights i stat unit vam daju prosecne vrednosti.

5. Zapisujte rezultate

Vodite tabelu/spreadsheet sa rezultatima profajliranja. Datum, opis scene, stat unit brojevi, GPU profajler breakdown. Kada napravite promenu, dodajte novi red. Ovo vam daje istoriju performansi projekta i jasnu sliku da li idete u dobrom ili losem pravcu.


39.3 GPU Bound vs CPU Bound vs Memory Bound

Tri fundamentalna tipa bottleneck-a

Svaki performansni problem u real-time renderovanju spada u jednu od tri kategorije. Identifikovanje u koju kategoriju spada vas problem je apsolutno prvi korak svake optimizacije. Ako ovo pogresite -- ako pretpostavite da ste GPU bound a zapravo ste CPU bound -- svaka optimizacija koju uradite bice uzaludna.

Hajde da detaljno prodjemo kroz svaku kategoriju.

GPU Bound: GPU ne stize da zavrsi renderovanje na vreme

Sta se desava: GPU prima sve instrukcije i podatke od CPU-a na vreme, ali mu treba previse vremena da zavrsi renderovanje frame-a. CPU zavrsi svoj posao i ceka da GPU zavrsi.

Tipicni uzroci:

Kako prepoznati:

Ukucajte stat unit u konzolu. Videcete:

Frame:    22.4 ms
Game:      4.2 ms
Draw:      5.1 ms
GPU:      21.8 ms  ← GPU je najsporiji, on definise ukupno vreme frame-a

Kada je GPU vreme znatno vece od Game i Draw vremena, vi ste GPU bound. CPU je zavrsio i ceka. GPU se muci.

Alati za dalju dijagnostiku:

CPU Bound: CPU ne stize da pripremi posao na vreme

Sta se desava: GPU je spreman, ali CPU ne stize da pripremi draw call-ove, azurira game logiku, procesira fiziku, AI, animacije... GPU zavrsava svoj posao i ceka da mu CPU da novi posao.

Tipicni uzroci -- Game Thread:

Tipicni uzroci -- Render Thread:

Kako prepoznati:

Frame:    22.4 ms
Game:     20.8 ms  ← Game thread je najsporiji
Draw:      6.2 ms
GPU:       9.1 ms

ili:

Frame:    22.4 ms
Game:      5.3 ms
Draw:     21.1 ms  ← Render thread je najsporiji
GPU:       8.7 ms

U oba slucaja, GPU je nezaposlen -- zavrsi brzo i ceka. Problem je na CPU strani.

Alati za dalju dijagnostiku:

Memory Bound: Memorija ne moze da isprati zahteve

Sta se desava: Sistem nema dovoljno memorije (RAM ili VRAM) ili memorijski bandwidth ne moze da isprati zahteve za podacima. Ovo se manifestuje na razlicite nacine zavisno od toga o kojoj memoriji govorimo.

VRAM (Video RAM) problemi:

Bandwidth problemi:

RAM (sistem memorija) problemi:

Kako prepoznati:

Najcesca Zabluda: "Imam previse trouglova"

Ovo zasluzuje posebnu sekciju jer je jedna od najcescih gresaka koje developeri prave.

Kada igra radi sporo, prva reakcija vecine ljudi je: "Sigurno imam previse trouglova u sceni." Ovo je intuitivno -- vise geometrije = vise posla = sporije. Zar ne?

Ne nuzno. I cesto -- ne.

Pogledajmo zasto:

Scenario A: Imate scenu sa 10 miliona trouglova ali 100 objekata. Nanite obradjuje geometriju efikasno, mesh-evi su grupisani, draw call-ova ima malo. GPU je happy.

Scenario B: Imate scenu sa 500,000 trouglova ali 5000 objekata. Svaki objekat je zaseban draw call. CPU render thread se gusi pripremajuci instrukcije za GPU. GPU zavrsi svaki batch za mikrosekunde i onda ceka sledeci.

Scenario B moze da bude dramaticno sporiji od Scenarija A, uprkos tome sto ima 20 puta manje trouglova. Razlog? Problem nije u broju trouglova -- problem je u broju draw call-ova, a to je CPU posao, ne GPU posao.

Ovo je fundamentalno vazno za razumevanje:

Metrika Ko je odgovoran Tip bottleneck-a
Broj trouglova (vertex processing) GPU GPU bound
Kompleksnost shadera (pixel processing) GPU GPU bound
Broj draw call-ova CPU (render thread) CPU bound
Game logika, fizika, AI CPU (game thread) CPU bound
Kolicina tekstura u memoriji VRAM Memory bound
Brzina citanja tekstura Memory bandwidth Memory/GPU bound

Kada neko kaze "moja scena ima previse trouglova", pitanje koje treba postaviti je: "Odakle znas? Da li si profajlirao?" Ako je odgovor "ne" -- onda ne znaju. Mozda imaju previse draw call-ova. Mozda imaju preskupe shadere. Mozda imaju memory bandwidth problem. Jedini nacin da saznaju je da izmere.

Vizuelni vodic za identifikaciju

Evo jednostavnog dijagrama za inicijalno odredjivanje tipa bottleneck-a:

Pokreni igru sa `stat unit`
            |
            v
   Koji red ima najvecu vrednost?
            |
    ┌───────┼───────────┐
    |       |           |
    v       v           v
  Game    Draw         GPU
    |       |           |
    v       v           v
  CPU      CPU        GPU
  Bound   Bound      Bound
 (game   (render    
 thread)  thread)   
    |       |           |
    v       v           v
  stat    stat       stat gpu
  game    scene      ProfileGPU
  stat    rendering  GPU profiler
  Insights stat      (RenderDoc,
           initviews  Nsight, PIX)

Zapamtite: ovo je pocetna dijagnoza. Realni projekti cesto imaju kombinovane bottleneck-ove -- delimicno GPU bound, delimicno CPU bound. Ali uvek postoji jedan koji je trenutno najgori, i to je onaj koji treba prvi resavati.


39.4 Performance Budgets: Planiranje Pre Problema

Koncept budzeta frame vremena

Ako ciljate 60 FPS, imate tacno 16.67 milisekundi da zavrsiste ceo frame. Sve -- game logika, rendering, post-processing, UI -- mora da stane u tih 16.67 ms.

Ako ciljate 30 FPS, imate 33.33 ms. Ako ciljate 120 FPS (za competitive igre ili VR), imate samo 8.33 ms.

Ciljani FPS     Budzet po frame-u
─────────────────────────────────
30 FPS          33.33 ms
60 FPS          16.67 ms
90 FPS (VR)     11.11 ms
120 FPS         8.33 ms

Ali ukupni budzet nije dovoljan. Morate ga podeliti na podsisteme, jer vise stvari se desava u toku jednog frame-a i sve moraju da stanu.

Podela budzeta izmedju threadova

U Unreal Engine 5, posao se deli na tri glavna thread-a:

Game Thread -- izvrsava gameplay logiku, fiziku, AI, animacije, Blueprint tick-ove, input processing.

Render Thread (Draw Thread) -- priprema rendering komande, sortira objekte, vrsi culling, salje draw call-ove GPU-u.

GPU -- izvrsava sve rendering operacije: vertex processing, rasterizacija, pixel shading, post-processing.

Ovi threadovi rade delimicno paralelno -- dok GPU renderuje frame N, CPU vec priprema frame N+1. Ali ako bilo koji thread prekoraci budzet, ceo pipeline se usporava.

Evo primera budzeta za 60 FPS igru:

Ukupni budzet: 16.67 ms
─────────────────────────────────────────────
Game Thread:    5.0 ms   (30% budzeta)
  - Gameplay logika:   2.0 ms
  - Fizika:            1.5 ms
  - AI:                1.0 ms
  - Animacije:         0.5 ms
  
Render Thread:  4.0 ms   (24% budzeta)
  - Visibility/Culling: 1.0 ms
  - Draw call priprema:  2.0 ms
  - Ostalo:              1.0 ms

GPU:           15.0 ms   (90% budzeta)*
  - Depth prepass:       1.0 ms
  - Base pass:           3.0 ms
  - Shadows:             2.5 ms
  - Lighting:            2.0 ms
  - GI (Lumen):          3.0 ms
  - Reflections:         1.5 ms
  - Translucency:        0.5 ms
  - Post-processing:     1.5 ms
─────────────────────────────────────────────

* GPU budzet moze da bude veci od ukupnog budzeta jer
  radi paralelno sa CPU-om za sledeci frame. Ali GPU
  vreme jednog frame-a ne sme da predje ~16 ms jer ce
  inace biti vidljiv "lag" od jednog frame-a.

Vazan detalj: Primetite da su Game Thread i Render Thread zbir manji od 16.67 ms, ali GPU ima 15 ms. To je zato sto CPU threadovi rade unapred -- dok GPU renderuje frame N, CPU vec priprema frame N+1. Ovo je pipelining. Ali GPU mora da zavrsi frame N pre nego sto moze da prikazati rezultat na ekranu, pa GPU vreme direktno odredjuje ukupni frame time za krajnjeg korisnika.

Per-Feature budzeti

Pored globalnog budzeta, iskusni timovi postavljaju budzetne limite za pojedinacne feature-e. Ovo sprecava da jedan sistem "pojede" resurse koje su drugi sistemi trebali.

Evo primera per-feature GPU budzeta za AAA akciono-avanturisticku igru na 60 FPS:

GPU Feature Budzet (od 15 ms ukupno):
─────────────────────────────────────────────
Depth / Z-Prepass          1.0 ms    (6.7%)
G-Buffer / Base Pass       3.0 ms   (20.0%)
Shadows (VSM)              2.5 ms   (16.7%)
Directional Lighting       0.8 ms    (5.3%)
Point/Spot Lights          1.2 ms    (8.0%)
Global Illumination        2.5 ms   (16.7%)
Reflections                1.5 ms   (10.0%)
Fog / Atmosphere           0.5 ms    (3.3%)
Translucency / Particles   0.8 ms    (5.3%)
Post-Processing            1.2 ms    (8.0%)
─────────────────────────────────────────────
UKUPNO                    15.0 ms  (100.0%)

Ovi brojevi su primer -- svaka igra ce imati drugacije prioritete. Horror igra ce mozda dati vise budzeta senkama i volumetrijskim efektima. Igra otvorenog sveta ce dati vise GI-ju i atmosphere rendering-u. Racing igra ce mozda imati veoma mali budzet za sve osim refleksija i motion blur-a.

Zasto budzetiranje treba raditi RANO

Ovo je greska koju pravi vecina timova: optimizacija se ostavlja za kraj projekta. "Napravicemo igru, pa cemo je optimizovati." Ovo je recept za katastrofu, i evo zasto:

Problem 1: Tehnicka arhitektura se kristalizuje rano

Ako ste proveli godinu dana praveci igru sa 500 dinamickih svetala u sceni jer "lepo izgleda", a onda shvatite da vam budzet za svetla dozvoljava samo 30 -- ne mozete jednostavno obrisati 470 svetala. Ceo art direction, ceo vizuelni stil igre je gradjen oko tih svetala. Morate da redizajnirate nivoe, sto je meseci posla.

Problem 2: Content pipeline se uspostavlja rano

Ako art tim provede godinu dana praveci teksture u 4K rezoluciji, sa svim mogucim mapama, a onda saznate da budzet ne dozvoljava toliko tekstura u memoriji -- ne mozete samo "smanjiti teksture". Morate da ih ponovo eksportujete, proverite kvalitet na nizoj rezoluciji, mozda ponovo radite UV mapping. To je meseci ponovljenog rada.

Problem 3: Performanse se degradiraju nelinearno

Performanse se ne degradiraju linearno kako dodajete sadrzaj. Dodavanje jednog svetla u scenu sa 10 svetala je prakticno besplatno. Dodavanje jednog svetla u scenu sa 200 svetala moze da bude katastrofalno jer ste vec na granici. Ako ne pratite budzet od pocetka, necete znati kad prelazite tu granicu.

Pravilni pristup: Postavite budzetne limite na pocetku projekta. Kada tim kreira sadrzaj, proverava ga uz budzet. "Ovaj nivo ima 3.2 ms senki -- to je 0.7 ms iznad budzeta, moramo smanjiti." Ova provera je trivijalna kada se radi u toku razvoja. Ista ta provera je katastrofalna kada se radi tri meseca pre izlaska igre.

Kako postaviti realisticne budzetne ciljeve

Postavljanje budzeta zahteva poznavanje ciljnog hardvera i vizuelnih ambicija projekta. Evo prakticnog procesa:

  1. Odredite ciljni hardver i FPS -- npr. PlayStation 5 na 60 FPS, ili Steam Deck na 30 FPS, ili PC GTX 1070 na 60 FPS.

  2. Napravite "stress test" scenu -- stavite u nju najzahtevniji sadrzaj koji ocekujete u igri. Profajlirajte. Ovo vam daje gornju granicu.

  3. Napravite "baseline" scenu -- minimalan sadrzaj (prazan nivo sa jednim svetlom). Profajlirajte. Ovo vam daje donju granicu (engine overhead).

  4. Razliku podelite na feature-e -- rasporedite raspolozive milisekunde po prioritetu. Feature-i koji su kriticni za vizuelni identitet igre dobijaju veci budzet.

  5. Ostavite marginu -- bar 10-15% budzeta ostavite nerasporeddjenim. Stvari se uvek pokazu skupljim nego sto ste planirali. Ova margina vas spasava od panicnog optimizovanja u poslednjem momentu.

  6. Revidirajte kvartalno -- budzet nije uklesan u kamen. Kako projekat napreduje, mozda cete shvatiti da ste precenili nesto a potcenili nesto drugo. Prilagodite.


39.5 Pravilo 80/20 u Optimizaciji

Pareto princip primenjen na performanse

Vilfredo Pareto, italijanski ekonomista, primetio je 1896. godine da 80% zemlje u Italiji poseduje 20% populacije. Ovaj princip -- poznat kao "Pareto princip" ili "Pravilo 80/20" -- pojavljuje se svuda u prirodi, ekonomiji, i softverskom inzenjerstvu.

U kontekstu optimizacije, pravilo 80/20 kaze: 80% performansnih problema dolazi od 20% koda ili sadrzaja. Obrnuto, 80% vaseg koda/sadrzaja trosli zanemarljivo malo resursa.

Sta to prakticno znaci? Znaci da kada otvorite profajler, videcete nesto ovako:

GPU Frame Breakdown:
─────────────────────────────────────────────
Lumen GI:           6.2 ms  ████████████████████
Shadows (VSM):      4.1 ms  █████████████
Base Pass:          2.3 ms  ███████
Post-Processing:    1.1 ms  ████
Translucency:       0.9 ms  ███
Fog:                0.4 ms  █
Depth Prepass:      0.3 ms  █
HZB:                0.2 ms  ▌
UI:                 0.1 ms  ▌
─────────────────────────────────────────────
UKUPNO:            15.6 ms

Dva sistema (Lumen GI i Shadows) trose 66% ukupnog GPU vremena. Ako ih optimizujete -- cak i umereno, recimo smanjite za 30% -- dobijate ustedu od ~3 ms. To je ogromno. To je razlika izmedju 64 FPS i 55 FPS (ili u ovom slucaju, od 15.6 ms pada na ~12.5 ms, sto je komforno ispod 16.67 ms za 60 FPS).

Sa druge strane, ako optimizujete HZB i UI (koji zajedno trose 0.3 ms), cak i ako ih magicno svedete na nulu, dobijate 0.3 ms ustede. Merljivo, ali nebitno.

Pronadjite velike pobede prvo

Kada pocnete sa optimizacijom, uvek idite od vrha ka dnu:

  1. Nadjite najskuplji sistem
  2. Pitajte: "Moze li ovo znacajno da se smanji?"
  3. Ako da -- optimizujte
  4. Profajlirajte ponovo
  5. Nadjite sledeci najskuplji sistem
  6. Ponovite

Ovaj pristup vam daje maksimalan rezultat za minimalan ulozeni napor. Umesto da provlacite fine cesljem kroz svaki sistem, fokusirate se na one koji zapravo koštaju.

Primer iz prakse

Pogledajmo konkretan primer. Imate igru koja radi na 40 FPS (25 ms po frame-u). Ciljate 60 FPS (16.67 ms). Trebate da ustevite ~8.3 ms.

Pre optimizacije:                      25.0 ms
─────────────────────────────────────────────────
Opcija A: Optimizovati "sve pomalo"
  - Smanjiti shadow rezoluciju:        -0.8 ms
  - Smanjiti polycount 20%:            -0.3 ms
  - Optimizovati particle sisteme:     -0.5 ms
  - Kompresovati teksture:             -0.2 ms
  - LOD podesavanja:                   -0.4 ms
  - Smanjiti post-processing:          -0.6 ms
  - Blueprint optimizacija:            -0.3 ms
  ────────────────────────────────────
  Ukupna usteda:                       -3.1 ms
  Rezultat:                            21.9 ms (45 FPS)
  Utroseno vreme:                      3 nedelje
  Status:                              JOS UVEK PRESPORO

Opcija B: Fokus na velike pobede
  - Profajler pokazuje: Lumen GI = 8.2 ms
  - Prebaciti sa "High" na "Medium":   -3.5 ms
  - Smanjiti Lumen Scene Detail:       -1.2 ms
  - Dodati Screen Probe rezoluciju:    -0.8 ms
  - Profajler sad pokazuje: Shadows = 4.5 ms
  - Smanjiti sa 4 kaskade na 3:        -1.0 ms
  - Shadow distance smanjenje:         -0.6 ms
  - Iskljuciti dinamicke senke za
    point lights na daljini:           -1.8 ms
  ────────────────────────────────────
  Ukupna usteda:                       -8.9 ms
  Rezultat:                            16.1 ms (62 FPS!)
  Utroseno vreme:                      3 dana
  Status:                              CILJ POSTIGNUT

Opcija A je intuitivni pristup -- "optimizuj sve pomalo". Trose tri nedelje i ne dostizu cilj. Opcija B je data-driven pristup -- identifikuj dva najveca sistema i fokusiraj se na njih. Trose tri dana i ne samo da dostizu cilj, vec imaju marginu za buduce feature-e.

Zakon opadajucih prinosa (Diminishing Returns)

Postoji tacka posle koje dalja optimizacija istog sistema donosi sve manje i manje poboljsanja. Ovo je zakon opadajucih prinosa.

Vreme ulozeno    Usteda     Kumulativno   Efikasnost
u optimizaciju              poboljsanje   (ms/sat)
────────────────────────────────────────────────────
  2 sata         3.0 ms      3.0 ms       1.50 ms/h  ← Lako! Ocigledne optimizacije
  4 sata         2.0 ms      5.0 ms       1.25 ms/h  ← Jos dobro
  8 sati         1.5 ms      6.5 ms       0.81 ms/h  ← Pocinje da opada
 16 sati         0.8 ms      7.3 ms       0.46 ms/h  ← Znacajno opada
 32 sata         0.3 ms      7.6 ms       0.24 ms/h  ← Jedva vidljivo
 64 sata         0.1 ms      7.7 ms       0.12 ms/h  ← Nije vredno

Primetite kako prvih 2 sata daju 3 ms poboljsanja, dok poslednjih 32 sata daju samo 0.1 ms. Ako ste vec ustedeli 7.3 ms i potrebno vam je ukupno 8 ms ustede -- bolje je da predjete na drugi sistem nego da trosiste jos 32 sata na ovaj.

Oportunitetni trosak

Svaki sat koji provedete optimizujuci je sat koji ne provodite na:

Optimizacija ima svoju cenu. Cilj nije da igra radi sa 500 FPS -- cilj je da radi sa dovoljno FPS-a da igracko iskustvo bude glatko. Sve iznad tog cilja je uzaludan trud (osim ako ne pravite benchmark ili competitive igru gde svaki frame racuna).

Pravilo palca: Kada vasa igra stabilno radi na ciljnom FPS-u sa 10-15% marginom, prestanite da optimizujete rendering i fokusirajte se na pravljenje bolje igre.


39.6 Optimizacioni Workflow: Korak po Korak

Sesti koraka koji se nikad ne preskacu

Svaka optimizaciona sesija treba da prati isti proces. Bez izuzetaka. Bez precica. Ovaj proces je razlog zasto iskusni developeri za dva dana postignu ono sto neiskusni ne postignu za dve nedelje.

Korak 1: Postavite cilj

Pre nego sto uopste otvorite profajler, morate da znate sta pokusavate da postignete. Cilj mora biti konkretan i merljiv:

Losi ciljevi:

Dobri ciljevi:

Cilj mora da ukljuci:

Korak 2: Profajlirajte trenutno stanje

Pokrenite igru na ciljnom hardveru. Nadjite najzahtevniji deo (worst case scenario). Zabelesite podatke:

=== Profiling Report: Level_03_BossFight ===
Datum: 2025-03-15
Hardver: RTX 3060 12GB, Ryzen 5 5600X, 16GB RAM
Rezolucija: 1920x1080, Quality: High
────────────────────────────────────────────────
stat unit:
  Frame:    28.4 ms (35 FPS)
  Game:      6.8 ms
  Draw:      5.2 ms
  GPU:      27.1 ms  ← GPU bound

stat gpu:
  Base Pass:           4.2 ms
  Shadow Depths:       3.8 ms
  Lighting:            2.1 ms
  Lumen Scene:         7.4 ms  ← Najveci krivac
  Lumen Reflections:   3.2 ms
  Translucency:        2.8 ms
  Post-Processing:     2.4 ms
  TSR:                 1.2 ms
────────────────────────────────────────────────
Cilj: 16.67 ms (60 FPS)
Potrebna usteda: 11.73 ms

Nikad ne preskacite ovaj korak. Bez njega, ne znate odakle pocinjete i ne mozete da merite napredak.

Korak 3: Identifikujte najveci bottleneck

Iz profajliranja jasno vidimo:

  1. Igra je GPU bound (GPU: 27.1 ms >> Game: 6.8 ms)
  2. Unutar GPU-a, Lumen Scene (GI) trosi 7.4 ms -- skoro 30% ukupnog GPU vremena
  3. Sledeci najveci su Shadow Depths (3.8 ms) i Lumen Reflections (3.2 ms)

Najveci bottleneck: Lumen Global Illumination sa 7.4 ms.

Korak 4: Adresirajte ga

Na osnovu identifikovanog bottleneck-a, primenjujete odgovarajuce optimizacije. Za Lumen GI, opcije ukljucuju:

Napravite jednu promenu, ne pet odjednom. Ako promenite pet stvari i performanse se poboljsaju, ne znate koja je pomogla. Mozda su tri pomoglee a dve pokvarile nesto.

Korak 5: Re-profajlirajte

Posle promene, profajlirajte ponovo. Na istom mestu. Sa istim uslovima.

=== Profiling Report: Level_03_BossFight (posle opt. 1) ===
Promena: Lumen Final Gather Quality 0.5 → 0.3
────────────────────────────────────────────────
stat unit:
  Frame:    23.1 ms (43 FPS)   ← Poboljsanje!
  Game:      6.9 ms
  Draw:      5.1 ms
  GPU:      22.0 ms

stat gpu:
  Lumen Scene:         4.8 ms  ← Sa 7.4 na 4.8 = -2.6 ms ustede
────────────────────────────────────────────────
Preostala usteda do cilja: 5.33 ms

Odlicno! Jedna promena je donela 2.6 ms ustede. Ali nismo na cilju -- treba jos 5.33 ms.

Korak 6: Ponovite dok ne dostignete cilj

Sada se vracate na Korak 3 sa novim profajlerskim podacima. Mozda je sada Shadow Depths novi najveci krivac. Adresirate ga. Profajlirate ponovo. I tako dalje, dok ne dostignete ciljnih 16.67 ms.

Iteracija  Promena                         GPU vreme  Usteda
──────────────────────────────────────────────────────────────
Pocetno    -                               27.1 ms    -
Iter 1     Lumen GI quality smanjenje       22.0 ms    -5.1 ms
Iter 2     Shadow cascade 4→3, distance     18.9 ms    -3.1 ms
Iter 3     Lumen Reflections quality        17.2 ms    -1.7 ms
Iter 4     Translucency: particle LOD       16.3 ms    -0.9 ms
Iter 5     Post-process: bloom quality      15.8 ms    -0.5 ms
──────────────────────────────────────────────────────────────
CILJ POSTIGNUT: 15.8 ms < 16.67 ms ✓
Margina: 0.87 ms (5.2%)

Pet iteracija. Pet merenja. Jasan, dokumentovan put od 35 FPS do 63 FPS.

Zasto se Koraci 2 i 5 NIKAD ne preskacu

Ponavljam ovo jer je toliko vazno da zasluzuje posebnu sekciju:

Korak 2 (inicijalno profajliranje) vam daje baseline -- polaznu tacku. Bez nje, nemate pojma koliko ste daleko od cilja i ne mozete da merite napredak.

Korak 5 (re-profajliranje) vam potvrdjuje da je promena zaista pomogla. Ponekad "optimizacija" zapravo pogorsava performanse. Na primer:

Bez re-profajliranja, nikada ne biste znali za ove probleme. Mislili biste da ste napravili poboljsanje, a zapravo ste napravili pogorasanje.


39.7 Anti-Patterni: Greske Koje Svi Prave

Anti-Pattern #1: "Optimizuj Sve"

Simptom: Developer otvara svaki asset, svaki Blueprint, svaki materijal i optimizuje ga -- smanjuje instruction count shadera, kompresuje teksture, dodaje LOD-ove -- bez obzira na to da li je taj asset uopste problem.

Zasto je pogresno: Ako materijal kosta 0.02 ms za renderovanje, cak i ako ga magicno svedete na nulu, ustedeli ste 0.02 ms. To je nemerljivo. A potrosili ste dva sata na to.

Ispravan pristup: Profajlirajte. Nadjite sta zapravo kosta. Optimizujte samo to.

Izuzetak: Na samom pocetku projekta, vredi uspostaviti best practices (razumne velicine tekstura, mesh LOD pipeline, organizovan Blueprint kod). Ali ovo nije "optimizacija" -- ovo je "higijena". Razlika je u tome sto higijenu radite iz principa a optimizaciju radite iz podataka.

Anti-Pattern #2: "To Mora Da Je Problem"

Simptom: Developer je apsolutno ubedjen da zna gde je bottleneck bez merenja. "Nanite je sigurno problem, ima previse trouglova." "Blueprint Tick je sigurno krivac, svi znaju da su Blueprinti spori."

Zasto je pogresno: Ljudska intuicija o performansama je notorno nepouzdana. Evo nekoliko primera koji demonstriraju zasto:

Ispravan pristup: Posmatrajte podatke, ne pretpostavke. Profajler ne laze.

Anti-Pattern #3: "Na mom racunaru radi savrseno"

Simptom: Developer testira igru na svom development PC-ju koji ima RTX 4090 i 64 GB RAM-a. Igra radi na 120+ FPS. "Performanse su odlicne!" A onda izadje na Steam i dobija negativne recenzije: "Radi na 15 FPS na mom GTX 1660."

Zasto je pogresno: Development masina je skoro uvek znatno jaca od prosecne masine igraca. A relativne performanse razlicitih feature-a se menjaju na razlicitom hardveru:

Ispravan pristup: Uvek testirajte na najslabijoj konfiguraciji koju podrzavate. To je vasa prava performansna slika. Ako to nije moguce, koristite Unreal-ov Scalability system na nizim podesavanjima da emulirate slabiji hardver (mada ovo nije savrsen zamenik za pravo testiranje).

Steam Hardware Survey vam daje podatke o tome sta vasi potencijalni igraci zapravo koriste. U momentu pisanja ove knjige, najcesca GPU klasa na Steam-u je jos uvek mid-range -- GTX 1650/1660 klasa ili RTX 3060. Vaša igra mora da radi dobro na njima.

Anti-Pattern #4: "Optimizovaćemo Kasnije"

Simptom: Tim zna da su performanse lose, ali ih stalno guraju u pozadinu. "Sada pravimo content, optimizovacemo kad zavrsimo." Meseci prolaze. Performanse se degradiraju sa svakim novim nivoom, svakim novim feature-om. Pred sam kraj produkcije, tim shvata da igra radi na 20 FPS i da je potrebna masivna optimizacija.

Zasto je pogresno: Optimizacija na kraju projekta je eksponencijalno teza nego tokom razvoja:

  1. Obim problema je veci -- Umesto jednog sistema koji treba optimizovati, imate dvadeset. Svaki je mali deo problema, ali zajedno su nepremostivi.

  2. Promene su rizicnije -- Na kraju projekta, sistemi su medjuzavisni. Promena jednog moze da pokvari pet drugih. A nemate vremena za regresijsko testiranje.

  3. Content mora da se ponovi -- Ako shvatite da morate da promenite lighting pipeline, sav sadrzaj koji je radjen sa starim pipeline-om mora da se revidira.

  4. Moral tima pada -- Niko ne zeli da provede poslednja tri meseca pre lansiranja briseci i kvarecci sadrzaj koji su tako naporno napravili.

Ispravan pristup: Postavite performance budget na pocetku. Profajlirajte redovno -- idealno nedeljno. Kada sistem predje budzet, odmah ga adresirate, dok je problem mali i izolovan.

Ovo ne znaci da optimizujete svaki dan. Znaci da pratite performanse svaki dan (ili barem svake nedelje) i reagujete kad se pojave problemi. Proaktivno pracenje je neuporedivo jeftinije od reaktivnog popravljanja.

Anti-Pattern #5: "Samo Smanji Broj Trouglova"

Simptom: Kad performanse padnu, prva (i cesto jedina) reakcija je: "Smanjite polycount." Art director pita "koliko trouglova je dozvoljeno?", dobije odgovor "sto manje", i pocne da sece geometriju. Karakteri gube detalje, okruzenje postaje uglasto, a poboljsanje performansi je... marginalno.

Zasto je pogresno: Ovo je posledica fundamentalnog nerazumevanja modernog rendering pipeline-a. U eri pre Nanite-a, broj trouglova je bio vazan faktor. Ali cak i tada, bio je samo jedan od mnogih. U eri Nanite-a, on je cesto najmanji problem.

Evo zasto "samo smanjiti trouglove" ne radi:

  1. Nanite upravlja geometrijom efikasno -- Nanite automatski radi LOD na nivou klastera, renderujuci samo onoliko trouglova koliko je potrebno za datu rezoluciju piksela. Dodavanje 10 miliona trouglova na Nanite mesh cesto kosta prakticno nista u performansama.

  2. Bottleneck je retko vertex processing -- Na modernim GPU-ovima, vertex processing je izuzetno brz. Bottleneck je skoro uvek pixel/fragment processing (kompleksni shaderi, overdraw) ili CPU-side (draw calls, game logic).

  3. Smanjivanje geometrije ima vizuelnu cenu -- Gubite siluetne detalje, povrsinske detalje, kvalitet normal-a. A ako bottleneck nije bio u geometriji, gubite vizuelni kvalitet bez dobiti u performansama. Najgori moguci trade-off.

  4. Pravi problemi su drugde -- Draw call count, shader complexity, shadow map rezolucija, lighting complexity, overdraw, memory bandwidth -- svaki od ovih moze da kosta visestruko vise od geometrije.

Ispravan pristup: Pitajte "zasto je sporo?" umesto "koliko trouglova imam?". Odgovor na prvo pitanje vam daje profajler. Odgovor na drugo pitanje je gotovo uvek irelevantan na modernom hardveru sa Nanite-om.

Anti-Pattern #6: "Kopirati Postavke iz Drugog Projekta"

Simptom: Developer nadje online tutorijal sa "optimalnim settings za UE5" i slepo kopira sve vrednosti u svoj projekat. Ili uzme .ini fajl iz jedne igre i preslika ga na drugu.

Zasto je pogresno: Svaki projekat je razlicit. Igra otvorenog sveta sa ogromnim landscape-ovima ima potpuno drugacije bottleneck-ove od linearnog horror-a u zatvorenom prostoru. Optimalne postavke za jednu su potpuno pogresne za drugu.

Na primer:

Ispravan pristup: Koristite tudje postavke kao polaznu tacku za eksperimentisanje, ali uvek profajlirajte u svom projektu i prilagodite na osnovu svojih podataka.

Anti-Pattern #7: "Ispravan Kod je Vazniji od Brzog Koda (Uvek)"

Simptom: Developer refaktorise savrseno funkcionalan (ali potencijalno neoptimalan) kod "jer nije dovoljno cist", menjajuci arhitekturu, dodajuci apstrakcije, primenjujuci design pattern-e -- i pritom pogorasavajuci performanse.

Zasto je pogresno: Citljivost i arhitekturalna cistoca jesu vazne. Ali u hot path-u (kodu koji se izvrsava hiljade puta po frame-u), performanse imaju primat. Nema svrhe imati savrseno citljiv kod ako igra radi na 15 FPS.

Ispravan pristup: Pisiti cist kod svuda osim u dokazanim hot path-ovima. Tamo pisiti brz kod i dokumentujte zasto izgleda neobicno. Komentar tipa // Using unrolled loop instead of range-for because profiler shows 0.3ms savings at 1000 iterations je sasvim prihvatljiv i buduca vas-verzija ce vam biti zahvalna.


39.8 Frame Time vs FPS: Zasto Milisekunde Menjaju Sve

Osvrt i produbljivanje

U Poglavlju 01, upoznali ste se sa konceptom frame time-a -- vremena potrebnog da se izracuna i prikaze jedan frame. Sada, u kontekstu optimizacije, moramo da dublje razumemo zasto je frame time (u milisekundama) dramaticno bolji alat za merenje od FPS-a.

Nelinearni odnos izmedju FPS i ms

FPS (frames per second) i frame time (ms) su inverzan odnos:

FPS = 1000 / frame_time_ms
frame_time_ms = 1000 / FPS

Ovo izgleda jednostavno, ali ima dubokih posledica. Pogledajte ovu tabelu:

FPS     Frame Time    Promena FPS    Promena u ms
────────────────────────────────────────────────────
200      5.00 ms
                       +100 FPS       -1.67 ms
100      10.00 ms
                        +40 FPS       -3.33 ms
 60      16.67 ms
                        +10 FPS       -3.33 ms
 50      20.00 ms
                        +10 FPS       -4.76 ms
 40      25.00 ms
                        +10 FPS       -8.33 ms
 30      33.33 ms
                        +10 FPS      -16.67 ms
 20      50.00 ms
                        +10 FPS      -50.00 ms
 10     100.00 ms

Pogledajte pazljivo. Ista promena od "plus 10 FPS" zahteva potpuno drugaciju kolicinu optimizacije zavisno od toga gde pocinjete:

Ovo je razlog zasto izvestaji poput "optimizovali smo igru sa 30 na 60 FPS" zvuce impresivno u FPS-ovima, ali su zapravo "ustedeli smo 16.67 ms" -- sto je impresivno ali merljivo. Dok "optimizovali smo sa 200 na 300 FPS" zvuci jos impresivnije, ali je zapravo usteda od samo 1.67 ms.

Zasto FPS laze

FPS je varljiv jer sugerise linearnost gde je nema. Evo prakticnog primera:

Radite optimizaciju. Napravite promenu koja stedi 2 ms po frame-u. Koliko FPS-ova ste "dobili"?

Ako ste bili na 30 FPS (33.33 ms):
  33.33 - 2.0 = 31.33 ms → 31.9 FPS
  "Dobili" ste 1.9 FPS. Zvuci lose.

Ako ste bili na 60 FPS (16.67 ms):
  16.67 - 2.0 = 14.67 ms → 68.2 FPS
  "Dobili" ste 8.2 FPS. Zvuci odlicno!

Ako ste bili na 120 FPS (8.33 ms):
  8.33 - 2.0 = 6.33 ms → 158 FPS
  "Dobili" ste 38 FPS. Zvuci neverovatno!

Ista optimizacija. Ista usteda od 2 ms. Ali "FPS poboljsanje" varira od 1.9 do 38 FPS zavisno od konteksta. FPS metrika je potpuno beskorisna za poredjenje optimizacija.

U milisekundama? Usteda je uvek 2 ms. Jasno, precizno, uporedivo.

Jitter i stabilnost

Postoji jos jedan razlog zasto je ms bolji od FPS-a: stabilnost frame time-a je kriticna za percepciju glatkog pokreta, i ms to mnogo bolje prikazuje.

Zamislite ove dve situacije:

Situacija A: Konstantnih 30 FPS (33.33 ms svaki frame)

Situacija B: Prosecnih 45 FPS, ali varira izmedju 20 FPS (50 ms) i 90 FPS (11.1 ms)

Situacija A izgleda glatko. Situacija B izgleda uzasno -- konstantno stuca, "drhti", igrac ima osecaj da igra baguje. A prosecni FPS je veci u Situaciji B!

Ovo je zato sto ljudski mozak ne primecuje prosecni FPS -- primecuje varijacije. Konzistentnih 30 FPS je neuporedivo bolji korisnicki dozivljaj od nestabilnih 45 FPS.

U milisekundama, ovo je ocigledno:

Zato, kada profajlirate, ne gledajte samo prosecni frame time -- gledajte i varijaciju (standardnu devijaciju). Unreal Insights vam prikazuje oba podatka.

Pravilo za komunikaciju rezultata

Kada komunicirate performansne rezultate -- bilo kolegama u timu, bilo publici -- uvek koristite milisekunde:

Lose: "Optimizovali smo igru sa 35 na 55 FPS" (koliki je napor bio? nejasno.)

Dobro: "Ustedeli smo 10.3 ms po frame-u (sa 28.6 ms na 18.3 ms)" (jasno kolika je usteda i koliko ste daleko od cilja)

Jos bolje: "Ustedeli smo 10.3 ms po frame-u, od cega 6.2 ms na GPU (shadows i GI), 3.1 ms na game thread (AI optimizacija), i 1.0 ms na render thread (draw call batching)" (precizno, informativno, ponovljivo)

Primena na VR i visoke FPS ciljeve

Ovo postaje posebno kriticno za VR i visoke FPS ciljeve:

Cilj           Budzet     "Slobodnih" ms ako imate 2ms ustede
──────────────────────────────────────────────────────────────
30 FPS         33.33 ms   Usteda je 6% budzeta
60 FPS         16.67 ms   Usteda je 12% budzeta
90 FPS (VR)    11.11 ms   Usteda je 18% budzeta
120 FPS        8.33 ms    Usteda je 24% budzeta

Isti 2 ms je "nice to have" na 30 FPS cilju, ali je masivno poboljsanje na 120 FPS cilju jer predstavlja skoro cetvrtinu ukupnog budzeta. Ovo pokazuje zasto je ms jedina metrika koja ima smisla -- isti napor ima isti rezultat u ms, ali drasticno razlicit "FPS improvement" zavisno od konteksta.


39.9 Mentalni Modeli za Optimizaciju

Optimizacija kao ekonomija

Jedan od najkorisnijih mentalnih modela za optimizaciju je razmisljanje u ekonomskim terminima. Svaki rendering feature ima cenu (u ms) i vrednost (vizuelni kvalitet). Optimizacija je pronalazenje najboljeg odnosa cene i vrednosti.

Feature          Cena (ms)    Vizuelna Vrednost    Odnos
──────────────────────────────────────────────────────────
Lumen GI          5.0 ms      Veoma visoka         Visok
VSM Shadows       3.0 ms      Visoka               Visok
TSR Upscaling     1.2 ms      Visoka               Veoma visok
Bloom             0.3 ms      Umerena              Visok
SSAO              0.8 ms      Umerena              Umeren
Motion Blur       0.5 ms      Niska-Umerena        Umeren
Chromatic Aberr.  0.1 ms      Niska                Umeren
Film Grain        0.1 ms      Minimalna            Nizak

Kada morate da ustedite milisekunde, iskljucujte feature-e sa najgorim odnosom cena/vrednost:

Feature-e sa dobrim odnosom (Lumen GI, TSR, senke) iskljucujete poslednje, jer gubite mnogo vizuelnog kvaliteta.

Optimizacija kao investigacija

Drugi koristan mentalni model: vi ste detektiv. Performansni problem je "zlocin". Profajler je vas alat za istrazivanje. Vi ne krivite prvog osumnjicenog -- vi pratite dokaze.

Ovaj pristup vas cuva od pristrasnosti. Kao sto dobar detektiv ne krivce na osnovu izgleda, dobar developer ne krivi na osnovu intuicije.

Optimizacija kao trijaža

Treci model, posebno koristan kada imate mnogo problema: medicinska trijaza.

U hitnoj pomoci, lekari ne lece pacijente po redu dolaska -- lece prvo one kojima je pomoc najhitnija i gde ce pomoc imati najveci efekat.

Isto tako, vi ne optimizujete po redu -- optimizujete prvo ono sto:

  1. Ima najveci uticaj na performanse (najveci bottleneck)
  2. Moze da se popravi u razumnom roku (izvodljivost)
  3. Nece pokvariti nesto drugo (rizik)
             Visok uticaj              Nizak uticaj
          ┌────────────────────┬────────────────────┐
Lako      │  URADITI ODMAH    │  Uraditi ako       │
izvodljivo│  (sweet spot)     │  ostane vremena    │
          ├────────────────────┼────────────────────┤
Tesko     │  Planirati        │  IGNORISATI        │
izvodljivo│  pazljivo         │  (waste of time)   │
          └────────────────────┴────────────────────┘

Feature-i u gornjem-levom uglu su vas prioritet. Feature-i u donjem-desnom uglu su gubljenje vremena.


39.10 Optimizacija kao Kontinuiran Proces

Zasto "jednokratna optimizacija" ne funkcionise

Mnogi timovi tretiraju optimizaciju kao dogadjaj -- "optimization sprint" od dve nedelje pred kraj projekta. Ovo skoro nikad ne funkcionise iz razloga koje smo vec diskutovali (obim problema, rizik promena, content rework).

Umesto toga, optimizacija treba da bude kontinuiran proces -- deo svakodnevnog workflow-a, ne poseban dogadjaj.

Performansna kultura

Najbolji timovi u industriji imaju ono sto se zove performansna kultura -- mentalitet gde svi u timu brinu o performansama, ne samo programeri:

Artisti:

Level Dizajneri:

Game Dizajneri:

Programeri:

Automatizovano performansno testiranje

Najnapredniji timovi postavljaju automatizovane performance test-ove koji se pokrecu sa svakim build-om:

  1. Automated camera paths -- fiksirana putanja kamere prolazi kroz kljucne delove svake mape
  2. Frame time recording -- frame time se belezi za svaki frame tokom putanje
  3. Regression detection -- ako prosecni frame time poraste za vise od X% u odnosu na prethodni build, build se oznacava kao "performance regression"
  4. Dashboard -- timski dashboard prikazuje performansne trendove tokom vremena

Ovo zvuci komplikovano, ali Unreal Engine ima podršku za ovo kroz Gauntlet automation framework i Unreal Insights export. Jednom kad se postavi, radi automatski i hvata performansne probleme cim se pojave, umesto nedelje ili mesece kasnije.

Kada prestati sa optimizacijom

Na kraju, vredno je razmotriti i ovo pitanje: kada je dovoljno?

Odgovor zavisi od konteksta, ali evo smernica:

  1. Stabilan frame time na ciljnom hardveru -- ne prosecni, vec stabilan. 99th percentile frame time treba da bude unutar budzeta.

  2. Dovoljno margine za buduce feature-e -- ako ste tacno na 16.67 ms, svaki novi feature ce vas gurnuti preko granice. Ciljajte 14-15 ms za 60 FPS da imate prostora za disanje.

  3. Vizuelni kvalitet je prihvatljiv -- nema svrhe imati 200 FPS ako igra izgleda kao da je iz 2005. Optimizacija ne sme da unisti vizuelni identitet igre.

  4. Svi target platforme su pokrivene -- ne samo vas dev PC, vec i minimalna specifikacija, konzole, Steam Deck, sta god da ciljate.

  5. Nema noticeable stutter-a -- stabilan frame time je vazniji od visokog prosecnog FPS-a. Ako imate 60 FPS sa povremenim 100 ms spike-ovima, morate da resiste spike-ove.

Kada su svi ovi uslovi ispunjeni -- prestanite da optimizujete i pravite bolju igru.


39.11 Optimizacija Kroz Razlicite Faze Razvoja

Pre-produkcija

U ovoj fazi:

Produkcija

U ovoj fazi:

Poliranje (Polish)

U ovoj fazi:

Post-lansiranje

Da, optimizacija ne prestaje sa lansiranjem:


39.12 Specificnosti Unreal Engine 5 Optimizacionog Ekosistema

Alati koji su vam na raspolaganju

UE5 dolazi sa bogatim setom alata za profajliranje i optimizaciju. Ovde ih samo navodimo -- detaljna upotreba svakog dolazi u narednim poglavljima:

Konzolne komande (brza dijagnoza):

Unreal Insights (dubinska analiza):

Viewport statistike (vizuelna dijagnoza):

Eksterni alati:

Scalability System

UE5 ima ugraddjen Scalability system koji automatski prilagoddjava kvalitet na osnovu performansi. Razumevanje ovog sistema je kriticno jer vam omogucava da podrzite sirok raspon hardvera sa jednim build-om:

Ovaj sistem vam omogucava da imate "Epic" kvalitet za RTX 4090 i "Low" kvalitet za GTX 1060, iz istog build-a.


Sumarni Pregled: Kljucni Termini

Pojam Znacenje
Bottleneck Usko grlo -- najsporiji deo sistema koji odredjuje ukupnu brzinu
GPU Bound Stanje gde GPU ne moze da zavrsi renderovanje na vreme; CPU ceka
CPU Bound Stanje gde CPU ne moze da pripremi posao na vreme; GPU ceka
Memory Bound Stanje gde memorijski kapacitet ili bandwidth ogranicava performanse
Frame Time Vreme potrebno da se izracuna i prikaze jedan frame, mereno u milisekundama
Performance Budget Unapred odredjeni limiti za vreme izvrsavanja pojedinih sistema (u ms)
Profiling Proces merenja performansi sistema radi identifikacije bottleneck-ova
Premature Optimization Optimizacija zasnovana na pretpostavkama umesto na merenjima; gubljenje vremena
Draw Call Jedna CPU instrukcija GPU-u da nacrta odredjeni set geometrije sa odredjenim materialom
Overdraw Renderovanje istog piksela vise puta zbog preklapanja objekata
stat unit UE5 konzolna komanda koja prikazuje osnovni breakdown frame vremena
ProfileGPU UE5 komanda za jednokratni detaljni GPU performance capture
Unreal Insights UE5 alat za dubinsku timeline analizu performansi svih threadova
Game Thread CPU thread koji izvrsava gameplay logiku, fiziku, AI, animacije
Render Thread CPU thread koji priprema rendering komande i salje ih GPU-u
Scalability UE5 sistem za automatsko prilagodjavanje kvaliteta razlicitom hardveru
Diminishing Returns Zakon opadajucih prinosa -- svaka sledeca optimizacija donosi manje poboljsanja
Frame Time Jitter Varijacija u trajanju frame-ova; uzrok percepcije "stucanja" cak i pri visokom prosecnom FPS-u
Pareto Princip (80/20) 80% performansnih problema dolazi od 20% koda/sadrzaja
Pipeline (CPU-GPU) Tehnika gde CPU priprema frame N+1 dok GPU renderuje frame N
Regression Pogorasanje performansi izazvano novom promenom u kodu ili sadrzaju

Povezivanje sa Ostalim Poglavljima

Ovo poglavlje je filozofski temelj za sve sto sledi u Delu 6:

Poglavlja koja su vec pokrivena i na koja se ovde referisemo:

Poglavlja koja slede i koriste ovu filozofiju:

Svako od narednih poglavlja ce pretpostavljati da razumete principe iz ovog poglavlja. Kada u Poglavlju 41 kazemo "profajlirajte pre i posle", nece biti daljih objasnjenja zasto -- to je vec pokriveno ovde. Kada kazemo "identifikujte da li ste GPU bound", nece biti objasnjenja sta to znaci -- vec znate.


Dalje citanje:

  • "Computer Architecture: A Quantitative Approach" -- Hennessy & Patterson (poglavlje o performansnom merenju i Amdahl-ovom zakonu, fundamentalno za razumevanje bottleneck-ova)
  • "The Art of Profiling" -- Epic Games, Unreal Fest prezentacija (prakticno uputstvo za profajliranje u UE5)
  • "Performance Optimization for Real-Time Applications" -- GDC prezentacije (godisnje, pokrivaju nove tehnike)
  • "Profiling and Optimizing UE5 Projects" -- Epic Games dokumentacija: docs.unrealengine.com/5.0/en-US/performance-and-profiling-in-unreal-engine/
  • "Unreal Insights Reference" -- Epic Games dokumentacija: docs.unrealengine.com/5.0/en-US/unreal-insights-in-unreal-engine/
  • "GPU Profiling and Optimization" -- NVIDIA Developer Blog (prakticni saveti za identifikaciju GPU bottleneck-ova)
  • "Understanding GPU Performance" -- AMD GPUOpen: gpuopen.com (analiza GPU performansi sa AMD perspektive)
  • "Real-Time Rendering, 4th Edition" -- Akenine-Moller, Haines, Hoffman (poglavlje o performansama i optimizaciji, sveobuhvatno)
  • "stat Commands Reference" -- Unreal Engine dokumentacija (kompletna lista stat komandi sa opisima)
  • "Scalability Reference" -- Epic Games dokumentacija: docs.unrealengine.com/5.0/en-US/scalability-reference-for-unreal-engine/

Sledece poglavlje (40): Sada kada razumete kako da pristupite optimizaciji, vreme je da zaronimo u konkretne alate i tehnike. Poglavlje 40 pokriva CPU profajliranje i optimizaciju -- kako koristiti Unreal Insights, kako identifikovati skupe Blueprint Tick-ove, kako smanjiti draw call overhead, i kako optimizovati game thread da nikad ne bude bottleneck.


Zapamtite: najbolji optimizator nije onaj koji zna sto trikova za ubrzavanje. Najbolji optimizator je onaj koji zna kako da pronadje pravi problem. Sve ostalo je mehanika.