Poglavlje 21: Shader Optimizacija
"Najbrzi shader je onaj koji nikada ne mora da se izvrsi. Drugi najbrzi je onaj koji radi sto manje posla da bi postigao isti vizuelni rezultat."
Stigli smo do jednog od najvaznijih poglavlja u citavoj knjizi. Mozete imati najlepse materijale na svetu, ali ako vam svaki frame traje 30 milisekundi samo zato sto su shaderi preskupi -- nista od toga. Igra mora da radi glatko, a shaderi su cesto najveci krivac za probleme sa performansama na GPU strani.
U prethodnim poglavljima naucili ste kako GPU zapravo radi (Poglavlje 08), kako shaderi funkcionisu i sta je branching (Poglavlje 17), i uskoro cete uciti o UE5 material sistemu u praksi (Poglavlje 22). Ovo poglavlje je most izmedu teorije i prakse -- ovde ucimo kako da merimo, analiziramo i optimizujemo shadere da bi nasi projekti radili brzo na ciljnom hardveru.
Hajde da krenemo.
21.1 Instruction Count -- Merenje Kompleksnosti Shadera
Sta je Instruction Count?
Svaki shader, kada se kompajlira, pretvara se u niz instrukcija koje GPU izvrsava. Instruction count je jednostavno broj tih instrukcija. Sto vise instrukcija shader ima, to vise posla GPU mora da obavi za svaki pixel (ili vertex) koji taj shader obradjuje.
Ali nije svaka instrukcija jednako "skupa". Postoje dve glavne kategorije:
ALU (Arithmetic Logic Unit) instrukcije -- to su matematicke operacije: sabiranje, mnozenje, deljenje, trigonometrijske funkcije, normalizacija vektora, itd. Ove instrukcije se izvrsavaju na compute jedinicama GPU-a.
TEX (Texture) instrukcije -- to su operacije citanja iz tekstura. Svaki Texture Sample node u vasem materijalu rezultuje jednom ili vise TEX instrukcija. Ove instrukcije zahtevaju pristup memoriji, sto moze biti sporije od cistog racunanja.
Kako Videti Instruction Count u UE5
Unreal Engine vam daje direktan uvid u instruction count vaseg materijala. Evo kako:
- Otvorite Material Editor
- U donjem levom uglu prozora videcete statistiku materijala
- Tu se prikazuje broj instrukcija, razvrstan po tipu
Videcete nesto poput:
Base pass shader: 127 instructions
62 ALU, 14 TEX, 51 other
Vertex shader: 23 instructions
Ovo vam odmah govori koliko je vas shader "tezak". Ali -- i ovo je bitno -- instruction count sam po sebi nije dovoljan za procenu performansi. Shader sa 200 instrukcija moze da radi brze od shadera sa 100 instrukcija, zavisno od toga koje su to instrukcije i kako GPU moze da ih paralelizuje.
Sta Se Smatra "Skupim"?
Ovo zavisi od konteksta -- koji GPU ciljate, kolika je rezolucija ekrana, koliko piksela taj materijal zauzima na ekranu. Ali evo nekih okvirnih smernica:
| Kategorija | Instruction Count (priblizno) | Komentar |
|---|---|---|
| Jednostavan | < 50 instrukcija | UI elementi, jednostavni unlit materijali |
| Umeren | 50-150 instrukcija | Tipicni PBR materijali sa par tekstura |
| Skup | 150-300 instrukcija | Kompleksni materijali sa blendingom, parallax |
| Veoma skup | 300-500 instrukcija | Landscape materijali sa mnogo layera |
| Opasno skup | > 500 instrukcija | Specijalni efekti, proceduralni materijali |
Zapamtite: ovi brojevi su okvirni. Shader sa 200 instrukcija koji se primenjuje na mali objekat u daljini je sasvim prihvatljiv. Isti shader na fullscreen quad-u koji pokriva ceo ekran moze da ubije performanse.
Instruction Count i Overdraw
Jedan koncept koji je kriticno vazan a cesto se previdi: overdraw. Ako imate dva poluprozirna objekta jedan iza drugog, GPU mora da izvrsi shader za oba, za svaki piksel gde se preklapaju. Ako imate pet slojeva translucent cestica -- to je pet puta vise shader izvrsavanja za te piksele.
Zato je instruction count posebno bitan za:
- Translucent materijale -- nemaju Z-buffer culling, pa se uvek renderuju
- Fullscreen efekte -- postprocess materijali koji obradjuju svaki piksel na ekranu
- Cestice/Particles -- cesto se preklapaju u velikim kolicinama
- Landscape materijale -- pokrivaju ogromne povrsine ekrana
Za opaque materijale na malim objektima, mozete sebi priustiti vise instrukcija. Za translucent materijale koji pokrivaju pola ekrana -- svaka instrukcija se racuna.
Kako UE5 Kompajlira Shadere
Kada napisete material u Material Editoru, UE5 ga kompajlira u HLSL (High Level Shading Language) kod, a zatim koristi platform-specificni kompajler da ga pretvori u strojni kod za ciljni GPU. Na Windows/DirectX 12, koristi se DXC (DirectX Shader Compiler). Za Vulkan, koristi se SPIR-V.
Mozete da vidite generisani HLSL kod tako sto otvorite Material Editor, kliknete na Window > HLSL Code (ili pritisnete odgovarajuci shortcut). Ovo je korisno kada zelite da razumete sta vas material zaista radi "ispod haube".
// Primer generisanog HLSL koda za jednostavan PBR material
float3 BaseColor = Texture2DSample(Material_Texture2D_0,
Material_Texture2D_0Sampler,
TexCoords).rgb;
float Roughness = Texture2DSample(Material_Texture2D_1,
Material_Texture2D_1Sampler,
TexCoords).r;
float Metallic = Texture2DSample(Material_Texture2D_1,
Material_Texture2D_1Sampler,
TexCoords).g;
Ovaj kod se zatim kompajlira u nesto poput:
sample_texture r0, t0, s0, v1.xy // TEX: sample base color
sample_texture r1, t1, s1, v1.xy // TEX: sample roughness/metallic
mov o0.rgb, r0.rgb // ALU: output base color
mov o1.r, r1.r // ALU: output roughness
mov o1.g, r1.g // ALU: output metallic
Primetite da imamo 2 TEX instrukcije i 3 ALU instrukcije. Ovo je krajnje pojednostavljen primer -- pravi shaderi su mnogo kompleksniji jer ukljucuju osvetljenje, senke, ambient occlusion, i mnogo toga drugog.
21.2 Texture Samples -- Cena Svakog Citanja
Osnove Texture Samplinga
Svaki put kada vas shader procita vrednost iz teksture, to je texture sample. Ova operacija izgleda jednostavno -- "daj mi boju na koordinati (u, v)" -- ali ispod povrsine se desava mnogo toga:
- Adresiranje: GPU racuna koja texel adresa odgovara datim UV koordinatama
- Filtering: Ako koristite bilinearni ili trilinearni filtering, GPU mora da procita 4 (bilinearni) ili 8 (trilinearni) texela i interpolira izmedu njih
- Mipmap selekcija: GPU odredjuje koji mipmap nivo da koristi na osnovu velicine piksela na ekranu
- Memory fetch: Podaci se citaju iz VRAM-a, prolaze kroz cache hijerarhiju
Svaki od ovih koraka ima svoju cenu. Ali najskuplji deo je obicno memory fetch -- pristup VRAM memoriji.
Koliko Texture Samplea je "Previse"?
Nema magicnog broja, ali evo prakticnih smernica:
| Platforma | Preporuceni max texture samples po shaderu |
|---|---|
| High-end PC (RTX 3080+) | 16-24 |
| Mid-range PC (GTX 1660) | 8-16 |
| Low-end PC / Stari hardware | 4-8 |
| Konzole (PS5/Xbox Series X) | 12-20 |
| Konzole (Switch) | 4-8 |
| Mobile (high-end) | 4-8 |
| Mobile (low-end) | 2-4 |
Ovi brojevi su za base pass -- glavno renderovanje. Ako imate i shadow pass, forward pass, itd., ukupan broj samplea po pikselu moze biti mnogo veci.
U tipicnom UE5 PBR materijalu, imate otprilike:
- Base Color texture: 1 sample
- Normal Map: 1 sample
- Roughness/Metallic/AO (packed): 1 sample
- Emissive (opciono): 1 sample
To su 3-4 samplea -- sasvim razumno. Problem nastaje kada:
- Koristite detail textures (dodatne teksture za blizinu): +2-4 samplea
- Imate landscape blending sa 4+ layera: 4 x 3 = 12+ samplea samo za jedan pass
- Koristite parallax occlusion mapping: moze dodati 16-64 samplea u petlji!
- Dodate subsurface scattering ili clear coat: dodatni samplea za svaki efekat
Dependent Texture Reads -- Tihi Ubica Performansi
Ovo je jedan od najvaznijih koncepata u shader optimizaciji, a mnogi ga ne razumeju.
Normalan (nezavisan) texture read:
UV koordinate su poznate unapred → GPU moze da prefetch-uje podatke
Dependent texture read:
UV koordinate zavise od rezultata prethodnog citanja → GPU mora da saceka
Hajde da ovo objasnimo detaljnije.
Moderni GPU-ovi imaju mogucnost prefetch-ovanja -- dok se izvrsavaju ALU instrukcije, GPU u pozadini vec pocinje da cita sledece teksture iz memorije. Ovo radi odlicno kada GPU unapred zna odakle ce da cita (tj. UV koordinate su poznate).
Ali kod dependent texture read-a, UV koordinate za drugo citanje zavise od rezultata prvog citanja. GPU mora da:
- Izvrsi prvo citanje
- Saceka da podaci stignu iz memorije
- Izracuna nove UV koordinate na osnovu tih podataka
- Tek onda posalje zahtev za drugo citanje
- Opet saceka da ti podaci stignu
Ovo "cekanje" unistava paralelizam koji je kljucan za GPU performanse.
Primer 1: Parallax Mapping (DEPENDENT!)
// Korak 1: Citamo heightmap da odredimo offset
float height = Texture2DSample(HeightMap, Sampler, UV).r;
// Korak 2: Koristimo height da pomerimo UV koordinate
float2 newUV = UV + height * viewDir.xy;
// Korak 3: Citamo base color sa NOVIM UV koordinatama
// OVO JE DEPENDENT READ! GPU mora da saceka rezultat koraka 1.
float3 color = Texture2DSample(BaseColor, Sampler, newUV).rgb;
Primer 2: Flow Map za Vodu (DEPENDENT!)
// Citamo flow map da dobijemo smer toka vode
float2 flowDir = Texture2DSample(FlowMap, Sampler, UV).rg;
// Koristimo flow direction da distortujemo UV za normal mapu
// OVO JE DEPENDENT READ!
float3 normal = Texture2DSample(NormalMap, Sampler, UV + flowDir * Time).rgb;
Primer 3: Lookup Table (DEPENDENT!)
// Racunamo neku vrednost
float value = dot(Normal, LightDir);
// Koristimo tu vrednost kao UV za LUT teksturu
// OVO JE DEPENDENT READ!
float3 ramp = Texture2DSample(RampTexture, Sampler, float2(value, 0)).rgb;
Svi ovi primeri su legitimni shader efekti, ali morate biti svesni njihove cene. Jedan dependent read nece napraviti veliku razliku. Pet ili sest u nizu -- to vec moze biti ozbiljan problem.
Kako Minimizovati Texture Samples
1. Channel Packing
Umesto da imate odvojene teksture za Roughness, Metallic, i Ambient Occlusion, spakovajte ih u kanale jedne teksture:
Tekstura "ORM":
R kanal = Occlusion (AO)
G kanal = Roughness
B kanal = Metallic
Rezultat: 3 texture samplea pretvorena u 1!
Evo tipicnog channel packing-a za PBR materijale:
| Tekstura | R | G | B | A |
|---|---|---|---|---|
| BaseColor | Red | Green | Blue | (opciono: Opacity) |
| Normal | Normal.X | Normal.Y | (izracunato) | -- |
| ORM | Occlusion | Roughness | Metallic | (opciono: Height) |
Sa ovim pristupom, kompletan PBR material sa AO, Roughness, Metallic i height mapom zahteva samo 3 texture samplea umesto 5-6.
2. Computed Patterns Umesto Tekstura
Mnoge jednostavne paterne mozete izracunati matematicki umesto da koristite teksturu:
// UMESTO texture samplea za grid patern:
// float grid = Texture2DSample(GridTexture, Sampler, UV).r;
// KORISTITE racunanje:
float2 gridUV = frac(UV * 10.0); // 10x10 grid
float grid = step(0.05, gridUV.x) * step(0.05, gridUV.y);
// Rezultat: identican grid patern, nula texture samplea!
// UMESTO noise teksture za jednostavan noise:
// float noise = Texture2DSample(NoiseTexture, Sampler, UV).r;
// KORISTITE proceduralni noise:
float noise = frac(sin(dot(UV, float2(12.9898, 78.233))) * 43758.5453);
// OPREZ: ovo je veoma jednostavan hash, ne pravi Perlin noise
// Za kvalitetniji noise, tekstura je cesto bolja opcija
3. Koristite Manje Tekstura Generalno
Postavite sebi pitanje za svaku teksturu:
- Da li mi ova tekstura zaista treba?
- Mogu li da koristim vertex color umesto teksture za blending?
- Mogu li da koristim konstantnu vrednost umesto teksture? (npr. Metallic = 0 za nemetalne povrsine)
- Mogu li da delim teksturu izmedju vise materijala?
Svaka tekstura koju eliminisete je jedan texture sample manje po pikselu, za svaki frame, za svaki objekat koji koristi taj material. To se brzo sabira.
4. Texture Atlasi
Umesto mnogo malih tekstura, koristite jednu veliku teksturu (atlas) sa razlicitim delovima na razlicitim UV koordinatama. Ovo ne smanjuje broj texture samplea direktno, ali moze poboljsati cache koherenciju jer GPU cita iz jedne teksture umesto iz vise.
Medjutim, budite pazljivi sa texture atlasima -- mipmapping moze da napravi probleme na ivicama izmedju delova atlasa (bleeding artefakti). UE5 ima podrsku za Virtual Texturing koji resava neke od ovih problema.
5. Virtual Texturing (VT)
UE5 podrzava Runtime Virtual Texturing (RVT) koji moze znacajno pomoci sa landscape materijalima. Umesto da svaki landscape layer ima svoje teksture koje se blend-uju u pixel shaderu, VT moze da "ispece" (bake) rezultat blendinga u virtualnu teksturu, efektivno pretvarajuci kompleksan multi-layer material u jednostavan single-texture lookup.
Ovo je posebno korisno za landscape materijale gde biste inace imali 12-20+ texture samplea.
21.3 ALU Bound vs TEX Bound -- Identifikacija Bottleneck-a
Dva Razlicita Bottleneck-a
Kada optimizujete shadere, kriticno je da razumete gde je vas bottleneck. GPU ima dva glavna resursa koje shaderi koriste:
- Compute jedinice (ALU) -- izvrsavaju matematicke operacije
- Memorijski bandwidth (TEX) -- cita podatke iz tekstura
Ova dva resursa rade paralelno. Dok ALU jedinice racunaju, memorijski kontroler moze da fetch-uje teksture. Idealan shader drzi oba sistema zauzeta -- nema cekanja.
Ali u praksi, jedan od ova dva sistema je obicno bottleneck:
ALU Bound (Ogranicen Racunanjem)
Vas shader radi previse matematike. GPU compute jedinice su 100% zauzete, a memorijski sistem ceka -- mogao bi da fetch-uje vise tekstura, ali nema sta da radi jer shader ne trazi teksture.
Simptomi ALU bound shadera:
- Visok instruction count sa malo texture samplea
- Smanjivanje rezolucije tekstura ne pomaze (jer problem nije u memoriji)
- Smanjivanje broja matematickih operacija pomaze
- Cesti kod proceduralnih materijala, kompleksnih matematickih efekata
Primeri ALU bound situacija:
Proceduralni noise sa vise oktava:
50+ ALU instrukcija po oktavi
0 texture samplea
→ Cist ALU bottleneck
Kompleksna Fresnel kalkulacija:
30+ ALU instrukcija
0 texture samplea
→ ALU bound
Toon shading sa vise rampi racunatih matematicki:
40+ ALU instrukcija
2-3 texture samplea
→ Verovatno ALU bound
TEX Bound (Ogranicen Memorijom)
Vas shader cita previse tekstura. Memorijski bandwidth je zasicen, a compute jedinice cekaju -- mogle bi da racunaju vise, ali nemaju podatke na kojima bi radile.
Simptomi TEX bound shadera:
- Mnogo texture samplea (15+)
- Smanjivanje rezolucije tekstura pomaze (manji podaci = brzi fetch)
- Dodavanje matematickih operacija ne usporava (jer compute jedinice ionako cekaju)
- Uklanjanje texture samplea pomaze
- Cesti kod landscape materijala, materijala sa mnogo layera
Primeri TEX bound situacija:
Landscape sa 4 layera, svaki sa BaseColor + Normal + ORM:
4 x 3 = 12 texture samplea minimum
+ macro texture + weightmap = 14-16 samplea
→ Gotovo sigurno TEX bound
Character sa diffuse + normal + ORM + detail normal + subsurface:
5-7 texture samplea
+ shadow map citanja
→ Moze biti TEX bound na slabijim GPU-ovima
Kako Identifikovati Bottleneck
Ovo je kljucna tehnika koju svaki graficki programer mora da zna:
Metod 1: Smanjite jedno, posmatrajte rezultat
Korak 1: Zabelezite baseline framerate
Korak 2: Smanjite broj ALU operacija (pojednostavite matematiku)
→ Ako framerate poraste ZNACAJNO: bili ste ALU bound
→ Ako framerate ostane ISTI: niste ALU bound
Korak 3: Vratite ALU na original
Korak 4: Smanjite broj texture samplea (uklonite neke teksture)
→ Ako framerate poraste ZNACAJNO: bili ste TEX bound
→ Ako framerate ostane ISTI: niste TEX bound
Metod 2: Smanjite rezoluciju tekstura
Promenite sve teksture sa 2048x2048 na 512x512. Ako performanse znacajno porastu, verovatno ste TEX bound (jer su manji podaci brzi za citanje i bolje se cache-iraju).
Metod 3: UE5 Console Komande
UE5 ima korisne console komande za testiranje:
r.ScreenPercentage 50 // Renderuje na pola rezolucije
// Ako ovo MNOGO pomaze, shader je skup po pikselu
r.MipMapLODBias 4 // Forsira najnize mipmap nivoe (male teksture)
// Ako ovo pomaze, TEX bound
stat gpu // Prikazuje GPU timing po pass-u
stat unit // Prikazuje ukupne timinge (Game, Draw, GPU)
Metod 4: GPU Profiler (RenderDoc, Nsight, PIX)
Za detaljnu analizu, koristite spoljne alate:
- RenderDoc (besplatan, cross-platform) -- moze da snimi frame i analizira svaki draw call
- NVIDIA Nsight Graphics -- za NVIDIA GPU-ove, daje detaljne metrike ukljucujuci ALU vs TEX utilizaciju
- PIX (za Windows/Xbox) -- Microsoft-ov GPU profiler
- AMD Radeon GPU Profiler -- za AMD GPU-ove
Ovi alati mogu da vam kazu tacno koliko vremena GPU provodi na ALU operacijama vs cekanje na memoriju, sto je neuporedivo preciznije od "probaj-pa-vidi" metoda.
Balansiran Shader
Idealan shader je balansiran -- ALU i TEX su podjednako zauzeti. Dok GPU cita teksturu iz memorije (sto traje vise taktova), compute jedinice rade matematiku. Kada matematika zavrsi, podaci iz teksture su vec stigli.
U praksi, ovo je tesko postici savrseno, ali mozete teziti tome:
- Ako ste ALU bound: mozete "besplatno" dodati teksturu ili dve (TEX ionako ceka)
- Ako ste TEX bound: mozete "besplatno" dodati matematiku (ALU ionako ceka)
Ovo je kontra-intuitivan koncept, ali je izuzetno koristan. Ponekad dodavanje posla zapravo ne usporava shader -- ako je taj posao na drugom resursu od onog koji je bottleneck.
21.4 Shader Complexity Visualization
Sta je Shader Complexity View?
UE5 ima ugradjeni rezim vizualizacije koji vam prikazuje koliko je svaki piksel na ekranu "skup" za renderovanje. Ovo je jedan od najkorisnijih alata za shader optimizaciju.
Da biste ga aktivirali:
- U Viewport-u, kliknite na padajuci meni za vizualizaciju (gore levo, gde pise "Lit")
- Izaberite "Optimization Viewmodes" > "Shader Complexity"
- Alternativno: koristite shortcut
Alt+8u editoru
Kako Citati Boje
Shader Complexity koristi toplinski (heat map) kolorni sistem:
| Boja | Znacenje | Instruction Count (priblizno) |
|---|---|---|
| Tamno zelena | Veoma jeftino | < 20 instrukcija |
| Zelena | Jeftino | 20-50 instrukcija |
| Svetlo zelena | Umereno | 50-100 instrukcija |
| Zuta | Skupo | 100-200 instrukcija |
| Narandzasta | Veoma skupo | 200-300 instrukcija |
| Crvena | Opasno skupo | 300-500 instrukcija |
| Tamno crvena | Kriticno skupo | 500-700 instrukcija |
| Bela/Rozi | Alarm! | 700+ instrukcija |
Evo kako ovo izgleda u praksi:
[Tipicna scena u Shader Complexity View]
Nebo: ZELENO (jednostavan gradient, jeftino)
Teren/Landscape: ZUTO do NARANDZASTO (mnogo layera, skupo)
Obican zid: ZELENO (1-2 teksture, jeftino)
Character: ZELENO do ZUTO (PBR sa subsurface)
Translucent cestice: CRVENO do BELO (overdraw!)
Staklo: NARANDZASTO (translucent + refleksije)
Voda: CRVENO (refleksije + refrakcije + flow maps)
Postprocess zone: Dodaje se na sve (ceo ekran postaje "skuplji")
Shader Complexity sa Quads
Postoji i poseban rezim: "Shader Complexity with Quads". Ovaj rezim dodatno prikazuje quad overdraw -- situaciju gde GPU trosi resurse na piksele koji su zapravo van trougla (jer GPU obradjuje piksele u 2x2 blokovima, pa mali trouglovi imaju mnogo "bespotrebnih" piksela).
Ovo je posebno bitno za scene sa mnogo malih trouglova -- npr. gusta vegetacija u daljini.
Sta Shader Complexity Prikazuje
- Instruction count svakog pixel shadera koji se izvrsava na tom pikselu
- Overdraw -- ako je vise shadera renderovano na istom pikselu, boje se akumuliraju
- Translucency cost -- translucent objekti dodaju svoju cenu na sve iza sebe
Sta Shader Complexity NE Prikazuje
Ovo je jednako vazno:
- Ne prikazuje stvarno GPU vreme -- shader sa 100 instrukcija ne traje nuzno duplo duze od shadera sa 50 instrukcija (zavisi od tipa instrukcija, cache koherencije, itd.)
- Ne uzima u obzir rezoluciju tekstura -- shader koji cita 4K teksture i shader koji cita 256x256 teksture pokazuju istu "cenu"
- Ne prikazuje vertex shader cost -- fokusiran je na pixel shader
- Ne uzima u obzir bandwidth -- dva shadera sa istim instruction count-om mogu imati drasticno razlicite performanse ako jedan cita iz cache-a a drugi ne
- Ne prikazuje compute shader cost -- Lumen, Nanite, i drugi sistemi koriste compute shadere koji se ne vide u ovom view-u
- Ne prikazuje realno opterecenje -- jer ne zna koliko piksela svaki materijal zauzima u finalnom frame-u na ciljnom hardveru
Kako Koristiti Shader Complexity Efektivno
Koristite ga kao PRVU trijazu, ne kao jedinu dijagnozu.
- Otvorite Shader Complexity View
- Identifikujte oblasti koje su crvene ili bele
- Kliknite na objekte u tim oblastima da vidite koji materijal koriste
- Otvorite taj materijal i analizirajte instruction count
- Koristite GPU profiler za stvarne timinge
Pratite relativan odnos boja:
- Ako je vecina scene zelena a jedan objekat je crven -- taj objekat treba paznju
- Ako je cela scena zuta -- to moze biti OK na high-end PC-u, ali problematicno za mobile
Posebno obratite paznju na:
- Velike crvene oblasti -- materijal koji je skup I pokriva mnogo piksela = veliki uticaj
- Bele tackice -- cestice ili mali translucent objekti sa overdraw-om
- Landscape -- cesto iznenadjenujuce skup zbog broja layera
Drugi Korisni Visualization Modovi
Pored Shader Complexity, UE5 nudi i druge korisne vizualizacije:
Light Complexity (Optimization Viewmodes > Light Complexity)
- Prikazuje koliko svetala utice na svaki piksel
- Zeleno = malo svetala, crveno = mnogo
Lightmap Density (Optimization Viewmodes > Lightmap Density)
- Prikazuje gustinu lightmapa na povrsinama
- Pomaze da identifikujete objekte sa prevelikim ili premalim lightmap rezolucijama
Stationary Light Overlap (Optimization Viewmodes > Stationary Light Overlap)
- Prikazuje gde se preklapaju stationary svetla (max 4 overlap)
- Crveno znaci da imate previse preklapajucih stationary svetala
21.5 Sta Kosta u Shaderima -- Detaljan Pregled
Tabela Relativnih Cena Operacija
Evo detaljne tabele sa relativnim cenama razlicitih shader operacija. Cene su priblizne i variraju izmedju GPU arhitektura, ali relativni odnosi su generalno tacni:
| Operacija | Relativna cena | Kategorija | Napomene |
|---|---|---|---|
add, sub (sabiranje, oduzimanje) |
1x | ALU | Najjeftinija operacija |
mul (mnozenje) |
1x | ALU | Jednako jeftino kao sabiranje na modernim GPU |
mad (multiply-add: a*b+c) |
1x | ALU | Jedna instrukcija za dve operacije! |
min, max, clamp, saturate |
1x | ALU | Besplatno ili gotovo besplatno |
abs, negate |
0x (besplatno) | ALU | Source modifier, ne zauzima ALU |
mov (kopiranje) |
0-1x | ALU | Cesto eliminisano optimizacijom |
dot (dot product) |
1x | ALU | Hardverski podrzano, veoma brzo |
cross (cross product) |
2-3x | ALU | Par mul+mad operacija |
lerp (linearna interpolacija) |
1x | ALU | Obicno se mapira na mad |
step |
1x | ALU | Jednostavno poredjenje |
smoothstep |
3-4x | ALU | Ukljucuje mnozenje i sabiranje |
rcp (reciprocal, 1/x) |
1x | ALU | Brzo na vecini GPU-ova |
rsqrt (1/sqrt(x)) |
1-2x | ALU | Specijalizovan hardver |
sqrt |
2-3x | ALU | Obicno rsqrt + rcp |
normalize |
3-4x | ALU | dot + rsqrt + mul |
length |
2-3x | ALU | dot + sqrt |
div (deljenje) |
2-4x | ALU | Obicno rcp + mul |
frac |
1x | ALU | Brzo na vecini GPU |
floor, ceil, round |
1x | ALU | Hardverski podrzano |
fmod (modulo) |
2-4x | ALU | Obicno vise operacija |
sin, cos |
4-8x | ALU | Transcendentalna -- skupa! |
tan |
8-12x | ALU | sin/cos -- duplo skupa |
asin, acos, atan |
8-16x | ALU | Veoma skupa! |
atan2 |
10-20x | ALU | Najskuplja trigonometrija |
pow (stepen) |
4-8x | ALU | exp2(y * log2(x)) |
exp, exp2 |
4-8x | ALU | Transcendentalna |
log, log2 |
4-8x | ALU | Transcendentalna |
Texture2DSample |
4-20x+ | TEX | Zavisi od cache-a! |
Texture2DSample (dependent) |
20-100x+ | TEX | Blokira pipeline |
TextureCubeSample |
6-25x+ | TEX | Skuplje od 2D |
Texture3DSample (volume) |
8-30x+ | TEX | Skuplje od Cube |
| Branch (koherentan) | 1-2x | Control | Svi pikseli u warp-u idu istim putem |
| Branch (divergentan) | 2-10x+ | Control | Pikseli u warp-u idu razlicitim putevima |
Transcendentalne Funkcije -- Detaljno
Transcendentalne funkcije (sin, cos, pow, exp, log) zasluzuju posebnu paznju jer su znacajno skuplje od osnovne aritmetike.
Zasto su skupe?
GPU ima specijalizovane hardware jedinice za ove operacije, ali ih ima manje nego opste ALU jedinice. Kada mnogo piksela istovremeno treba sin(), ove specijalizovane jedinice postaju bottleneck.
Neke GPU arhitekture (posebno starije i mobilne) koriste aproksimacije kroz serije operacija umesto specijalizovanog hardvera, sto ih cini jos skupljim.
Ceste zamke:
// SKUPO: pow u petlji
for (int i = 0; i < 8; i++) {
result += pow(something, 2.0 + i); // pow se poziva 8 puta!
}
// SKUPO: sin za animaciju na svakom pikselu
float wave = sin(Time * 3.14159 + UV.x * 10.0); // sin po pikselu!
// SKUPO: normalize na svakom pikselu kada nije potrebno
float3 dir = normalize(someVector); // 3-4 ALU operacije
// Ako vam ne treba jedinicni vektor, ne normalizujte!
Jeftinije alternative:
// UMESTO pow(x, 2.0): koristite x * x
float result = x * x; // 1x umesto 4-8x
// UMESTO pow(x, 4.0): koristite (x*x)*(x*x)
float x2 = x * x;
float result = x2 * x2; // 2x umesto 4-8x
// UMESTO pow(x, 0.5): koristite sqrt(x)
float result = sqrt(x); // 2-3x umesto 4-8x
// UMESTO sin() za jednostavnu oscilaciju: koristite triangle wave
float triangle = abs(frac(Time) * 2.0 - 1.0); // ~2x umesto 4-8x
// UMESTO normalize() kada treba samo smer:
float3 dir = someVector * rsqrt(dot(someVector, someVector)); // ekvivalentno
// Ali kompajler obicno vec ovo radi za normalize -- proverite!
Texture Sample Cena -- Detaljno
Texture sample je posebna zver. Njegova "cena" varira enormno zavisno od okolnosti:
Cache Hit (najbolji slucaj): ~4-8 taktova
- Podaci su vec u L1 ili L2 cache-u GPU-a
- Desava se kada susedni pikseli citaju bliske texele
- Dobar mipmapping pomaze (manji mip = vise texela u cache-u)
Cache Miss (najgori slucaj): ~200-800 taktova
- Podaci moraju da se citaju iz VRAM-a
- Desava se kod nasumicnog pristupa, velikih tekstura bez mipmapping-a
- Dependent texture reads cesto uzrokuju cache miss-eve
Sto utice na cache performanse:
DOBRO za cache:
+ Bilinearni filtering (cita 4 susedna texela)
+ Koriscenje mipmapa (manji podaci, bolji cache)
+ Sekvencijalan pristup (susedni pikseli citaju susedne texele)
+ Manje teksture (cela tekstura staje u cache)
LOSE za cache:
- Nasumicne UV koordinate (skakanje po teksturi)
- Ogromne teksture bez mipmapa
- Dependent reads (nepredvidiv pristup)
- Trilinearni filtering (8 texela umesto 4)
- Anizotropni filtering (do 16 texela po sample!)
Anizotropni filtering (AF) zasluzuje poseban komentar. Kada gledate povrsinu pod uglom (npr. pod koji se prostire u daljinu), AF poboljsava kvalitet filtriranja tako sto uzima vise uzoraka duz jedne ose. AF 16x znaci do 16 uzoraka po jednom "texture sample" -- sto moze znacajno povecati TEX cost.
Branching -- Kontekst je Sve
O branching-u smo detaljno govorili u Poglavlju 17, ali hajde da rezimiramo u kontekstu optimizacije:
Koherentan branch (svi pikseli u warp-u idu istim putem):
// Ovo je JEFTINO jer su svi pikseli koherentni
if (MaterialID == 1) {
// svi pikseli ovog materijala idu ovde
color = red;
} else {
color = blue;
}
Cena: minimalna, samo branch instrukcija.
Divergentan branch (pikseli u istom warp-u idu razlicitim putevima):
// Ovo je SKUPO jer pikseli u istom warp-u divergiraju
if (UV.x > 0.5) {
// neki pikseli idu ovde
color = complexCalculation1();
} else {
// drugi pikseli idu ovde
color = complexCalculation2();
}
Cena: GPU izvrsava OBA puta za sve piksele u warp-u, maskirajuci rezultate. Efektivna cena je zbir obe grane.
Prakticna pravila za branching u shaderima:
- Branch na uniform vrednosti (ista za sve piksele) je gotovo besplatan
- Branch na vertex interpoliranoj vrednosti koja se menja postepeno -- obicno koherentan, relativno jeftin
- Branch na per-pixel kalkulaciji sa nasumicnim rezultatom -- potencijalno divergentan, moze biti skup
- Branch je isplativ samo ako preskace DOVOLJNO posla -- za 2-3 instrukcije, bolje je uvek ih izvrsiti
Dependent Reads -- Detaljno
Vec smo pomenuli dependent reads u sekciji o texture samples, ali hajde da dublje razumemo zasto su toliko problematicni.
Normalan tok GPU pipeline-a:
Takt 1-2: GPU salje zahtev za teksturu A na UV koordinate (0.5, 0.3)
Takt 3-10: Dok ceka podatke iz memorije, GPU radi ALU operacije
Takt 8-12: Podaci za teksturu A stizu iz cache/memorije
Takt 9-10: GPU salje zahtev za teksturu B na UV koordinate (0.7, 0.2)
Takt 11-20: GPU radi vise ALU operacija
Takt 18-22: Podaci za teksturu B stizu
Ukupno: ~22 taktova, ali GPU je radio korisne stvari vecinu vremena.
Tok sa dependent read-om:
Takt 1-2: GPU salje zahtev za teksturu A na UV koordinate (0.5, 0.3)
Takt 3-12: GPU CEKA jer mu trebaju podaci iz A da bi znao UV za B
(ne moze da radi ALU jer nema sta da racuna)
Takt 12: Podaci za A stizu. GPU racuna UV za B.
Takt 13-14: GPU salje zahtev za teksturu B na IZRACUNATE UV koordinate
Takt 15-24: GPU OPET CEKA na podatke iz B
(opet ne moze nista korisno da radi)
Takt 24: Podaci za B stizu. Shader nastavlja.
Ukupno: ~24 taktova, ali GPU je bio idle vecinu vremena! Propustena je ogromna kolicina paralelizma.
U realnosti, GPU pokusava da maskira ovu latenciju prebacivanjem na drugi warp dok ceka, ali ako svi warps imaju dependent reads, nema gde da se prebaci i GPU ceka.
21.6 Kako Smanjiti Cenu Shadera
1. Uklonite Nekorisne Feature-e
Ovo je najjednostavnija i najefikasnija optimizacija: ne radite ono sto ne morate.
Normal mapa na ravnoj povrsini? Ako je povrsina potpuno ravna (npr. zid od jednog velikog poligona), normal mapa mozda ne dodaje dovoljno vizuelnog kvaliteta da opravda cenu. Razmislite da li vam zaista treba.
SA normal mapom: +1 texture sample, +~10 ALU (tangent space transform)
BEZ normal mape: Usteda od 1 TEX + 10 ALU po pikselu
Metallic mapa za nemetalne objekte?
Ako je objekat potpuno nemetalan (drvo, kamen, tkanina), ne treba vam metallic tekstura. Postavite Metallic = 0 kao konstantu.
AO mapa u prostoriji sa screen-space AO? Ako vec koristite SSAO ili GTAO, per-material AO mapa mozda nije neophodna, posebno na objektima u unutrasnjosti.
Detail textures na objektu koji se nikada ne vidi iz blizine? Ako je objekat uvek u srednjoj ili velikoj daljini, detail textures su bespotrebni trosak.
2. Koristite Jednostavniju Matematiku
Mnoge "skupe" operacije imaju jeftinije aproksimacije koje su vizuelno gotovo identicne.
Fresnel aproksimacija:
// TACNO ali skupo:
float fresnel = pow(1.0 - dot(Normal, ViewDir), 5.0);
// Cena: dot(1x) + sub(1x) + pow(4-8x) = ~6-10x
// SCHLICK APROKSIMACIJA (skoro identicno vizuelno):
float NdotV = dot(Normal, ViewDir);
float fresnel = pow(1.0 - NdotV, 5.0);
// Mozemo dalje optimizovati:
float x = 1.0 - NdotV;
float x2 = x * x;
float fresnel = x2 * x2 * x; // x^5 bez pow()!
// Cena: dot(1x) + sub(1x) + 3xmul = ~5x
// Usteda: ~50% od pow verzije
// JOS JEFTINIJE za mnoge slucajeve:
float fresnel = 1.0 - NdotV; // Linearni fresnel
// Cena: dot(1x) + sub(1x) = ~2x
// Nije fizicki tacno, ali za mnoge efekte izgleda OK
Brz sqrt za normalizaciju:
// UE5 Material Editor vec koristi optimizovane verzije,
// ali u custom HLSL kodu:
// UMESTO:
float len = length(vec); // sqrt(dot(vec,vec))
float3 normalized = vec / len; // skupo deljenje
// KORISTITE:
float invLen = rsqrt(dot(vec, vec)); // rsqrt je brz na GPU
float3 normalized = vec * invLen; // mnozenje umesto deljenja
Aproksimacija za pow sa malim eksponentima:
// pow(x, 2) = x * x // 1 mul
// pow(x, 3) = x * x * x // 2 mul
// pow(x, 4) = (x*x) * (x*x) // 2 mul (ne 3!)
// pow(x, 5) = (x*x) * (x*x) * x // 3 mul
// pow(x, 8) = ((x*x)*(x*x)) * ((x*x)*(x*x)) // 3 mul
3. Channel Packing Tekstura
Ovo smo vec pomenuli, ali zasluzuje detaljniji pregled jer je jedna od najefikasnijih tehnika.
Standardni UE5 PBR Setup (neoptimizovan):
Tekstura 1: Base Color (RGB) → 1 sample
Tekstura 2: Normal Map (RG) → 1 sample
Tekstura 3: Roughness (grayscale) → 1 sample
Tekstura 4: Metallic (grayscale) → 1 sample
Tekstura 5: AO (grayscale) → 1 sample
Tekstura 6: Height/Displacement → 1 sample
Tekstura 7: Emissive (RGB) → 1 sample
─────────────────────────────────────────────────
UKUPNO: 7 texture samplea
Optimizovan Setup sa Channel Packing:
Tekstura 1: Base Color (RGB) + Opacity (A) → 1 sample
Tekstura 2: Normal Map (RG) → 1 sample
Tekstura 3: ORM packed: → 1 sample
R = Ambient Occlusion
G = Roughness
B = Metallic
A = Height
Tekstura 4: Emissive (RGB) - samo ako treba → 0-1 sample
─────────────────────────────────────────────────
UKUPNO: 3-4 texture samplea (USTEDA: 3-4 samplea!)
Vazne napomene o channel packing:
- sRGB vs Linear: Base Color je u sRGB prostoru, ali Roughness/Metallic/AO su linear. NE MOZETE ih spakovati u istu teksturu! Zato se ORM pakuje odvojeno od Base Color-a.
- Kompresija: Razliciti kanali u istoj teksturi dele isti kompresioni format. Ako je Normal Map u BC5 formatu (dva kanala, visoka preciznost), ne mozete tu dodati treci kanal. Planirajte unapred.
- Alpha kanal: Neki kompresioni formati (BC1/DXT1) nemaju alpha kanal ili imaju samo 1-bitni alpha. BC3/DXT5 ima 8-bitni alpha. BC7 ima kvalitetan alpha ali je veci.
Preporuceni kompresioni formati za packed teksture:
| Tekstura | Format | Kanali | Napomena |
|---|---|---|---|
| Base Color | BC1 (DXT1) | RGB | Ako nema alpha |
| Base Color + Opacity | BC3 (DXT5) ili BC7 | RGBA | BC7 je kvalitetniji |
| Normal Map | BC5 | RG | Najbolji kvalitet za normale |
| ORM | BC1 (DXT1) | RGB | Ako nema height u A |
| ORM + Height | BC3 (DXT5) ili BC7 | RGBA | BC7 za bolji kvalitet |
4. Prebacite Kalkulacije iz Pixel u Vertex Shader
Vertex shader se izvrsava jednom po vertexu, a pixel shader jednom po pikselu. Na tipicnom mesu, imate mnogo vise piksela nego vertexova. Na primer:
Objekat sa 10,000 vertexova koji zauzima 500x300 piksela:
Vertex shader: 10,000 izvrsavanja
Pixel shader: 150,000 izvrsavanja
Odnos: 1:15 u korist vertex shadera!
Ako mozete da prebacite kalkulaciju iz pixel u vertex shader, ustedecete 15x izvrsavanja te kalkulacije (u ovom primeru).
Sta MOZE da se prebaci u vertex shader:
- Transformacije koordinata (world position, view direction)
- Kalkulacije koje zavise samo od pozicije vertexova
- Wind animacija, vertex offset
- Jednostavni Fresnel (kvalitet je nizi, ali cesto prihvatljiv)
- UV transformacije (tiling, offset, rotacija)
Sta NE MOZE (ili ne treba) da se prebaci:
- Per-pixel osvetljenje (linearno interpolirani rezultati izgledaju lose)
- Normal mapping (po definiciji per-pixel)
- Visoko-frekventni paterne (interpolacija ih izgladi)
- Bilo sta sto zahteva per-pixel preciznost
Kako u UE5 Material Editoru:
Koristite "Vertex Interpolator" cvor (Custom UV u starijim verzijama):
- Spojite izracunavanje na ulaz Vertex Interpolator-a
- Rezultat ce se izracunati u vertex shaderu
- GPU ce ga automatski interpolirati za svaki piksel
- Koristite izlaz Vertex Interpolator-a u ostatku materijala
[Vertex Shader]
TextureCoordinate → Multiply (Tiling) → [Vertex Interpolator Input]
[Pixel Shader]
[Vertex Interpolator Output] → Texture Sample → Base Color
Umesto:
TextureCoordinate → Multiply (Tiling) → Texture Sample → Base Color
(gde bi se Multiply racunao za svaki piksel)
U ovom jednostavnom primeru usteda je minimalna (jedan multiply), ali za kompleksnije UV kalkulacije (rotacija, world-aligned teksture, proceduralne UV) usteda moze biti znacajna.
5. Material Quality Switches
UE5 ima ugradjeni sistem za razlicite nivoe kvaliteta materijala. Mozete definisati razlicite verzije istog materijala za razlicite platforme ili kvalitetne postavke.
Quality Switch Node:
U Material Editoru, dodajte Quality Switch cvor. Ovaj cvor ima ulaze za razlicite nivoe kvaliteta:
- High -- koristite za PC/konzole na visokim postavkama
- Medium -- za srednje postavke ili slabije konzole
- Low -- za mobile ili veoma stari hardware
Quality Switch primer za Roughness:
HIGH: Texture Sample (2048x2048 roughness map) →
MEDIUM: Texture Sample (1024x1024 roughness map) → Quality Switch → Roughness
LOW: Constant (0.5, nema teksture uopste) →
Ovo vam omogucava da imate jedan material koji se automatski prilagodjava hardveru.
Material Quality Level se postavlja sa:
r.MaterialQualityLevel 0 // Low
r.MaterialQualityLevel 1 // Medium
r.MaterialQualityLevel 2 // High
r.MaterialQualityLevel 3 // Epic
Ovo generise odvojene shader permutacije za svaki nivo kvaliteta. Nema runtime cost-a za prebacivanje -- odgovarajuca permutacija se jednostavno koristi.
6. Precompute u Teksturama (Lookup Tables / LUT)
Ako imate skupu matematicku funkciju koja zavisi od jednog ili dva parametra, mozete da je "ispeczete" u teksturu i koristite texture sample umesto racunanja.
Primer: Custom Lighting Ramp
// SKUPO: Racunanje custom lighting rampe matematicki
float NdotL = dot(Normal, LightDir);
float lighting = smoothstep(-0.1, 0.1, NdotL) * 0.5 +
smoothstep(0.4, 0.6, NdotL) * 0.3 +
smoothstep(0.8, 0.9, NdotL) * 0.2;
// Cena: 3x smoothstep (3x4=12 ALU) + 2 mul + 2 add = ~16 ALU
// JEFTINO: Precomputed u 1D LUT teksturi
float NdotL = dot(Normal, LightDir);
float2 lutUV = float2(NdotL * 0.5 + 0.5, 0.5); // mapiranje iz [-1,1] u [0,1]
float lighting = Texture2DSample(LUT_Texture, Sampler, lutUV).r;
// Cena: 1 dot + 1 mad + 1 TEX = ~6 ukupno
// NAPOMENA: Ovo JE dependent read, ali je usteda u ALU velika
Primer: Atmosferski Scattering
// VEOMA SKUPO: Racunanje scattering-a u realnom vremenu
// (Rayleigh + Mie, visestruko integriranje)
// Moze da kosta 100+ ALU instrukcija
// JEFTINO: 2D ili 3D LUT
// Pretkorissite visinu (altitude) i ugao sunca kao UV koordinate
float2 lutUV = float2(altitude / maxAltitude, sunAngle / PI);
float3 scattering = Texture2DSample(ScatteringLUT, Sampler, lutUV).rgb;
// Cena: 2 div + 1 TEX = ~6-10 ukupno
Prednosti LUT-ova:
- Drasticno smanjuju ALU cost za kompleksne funkcije
- Moguce je imati "nemoguce" funkcije (rucno nacrtane rampe, art-directed krive)
- Lako se menjaju bez rekompilacije shadera
Mane LUT-ova:
- Zauzimaju teksturnu memoriju
- Ogranicena preciznost (8-bit LUT ima samo 256 vrednosti)
- Svaki LUT je jedan texture sample (dependent read!)
- Interpolacija moze da zagladi ostre prelaze
7. Koristite Manje Interpolatora
Interpolatori (varyings) su podaci koji se prenose iz vertex shadera u pixel shader. GPU automatski interpolira ove vrednosti za svaki piksel unutar trougla.
Svaki interpolator zauzima registar i bandwidth u rasterizer fazi. UE5 koristi ogranicen broj interpolatora, i kada ih je previse, kompajler mora da "pakuje" podatke, sto moze dodati ALU instrukcije za pakovanje/raspakovanjе.
Tipicni interpolatori u UE5 materijalu:
UV Set 0: 2 floata (interpolator 0)
UV Set 1: 2 floata (interpolator 1)
Vertex Color: 4 floata (interpolator 2)
World Position: 3 floata (interpolator 3)
World Normal: 3 floata (interpolator 4)
Tangent, Bitangent: 6 floatova (interpolator 5-6)
Custom UVs: 2+ floatova po custom UV setu
Moderni GPU-ovi obicno podrzavaju 16-32 interpolatora (svaki je vec4, dakle 4 floata). Ako predjete ovu granicu, shader ce imati dodatne instrukcije za pakovanje.
Kako smanjiti broj interpolatora:
- Ne koristite Custom UVs osim ako zaista trebate
- Ne koristite vise UV setova ako mozete da se snadjete sa jednim
- Ako ne koristite Vertex Color, ne zahtevajte ga u materijalu
21.7 Shader LOD -- Jednostavniji Materijali na Daljini
Koncept Shader LOD
Vec znate za mesh LOD -- smanjivanje broja poligona za objekte u daljini. Shader LOD je isti koncept primenjen na materijale: koristite jednostavniji (jeftiniji) shader za objekte koji su dalje od kamere.
Logika je jednostavna: ako je objekat daleko i zauzima samo 20 piksela na ekranu, nema smisla koristiti materijal sa parallax occlusion mappingom, detail teksturama, i 15 texture samplea. Na 20 piksela, ne mozete da vidite te detalje. A GPU i dalje placa punu cenu za svaki od tih piksela.
Material Quality Levels za LOD
UE5 omogucava da razliciti mesh LOD nivoi koriste razlicite materijale. Ovo je najdirektiji nacin za implementaciju shader LOD-a.
Primer setup-a:
LOD 0 (blizu): Material_Full
- Base Color (2048x2048)
- Normal Map (2048x2048)
- ORM packed (2048x2048)
- Detail Normal (1024x1024)
- Detail Tiling Mask
- Parallax Occlusion Mapping
→ 8 texture samplea, 200+ instrukcija
LOD 1 (srednje): Material_Medium
- Base Color (1024x1024)
- Normal Map (1024x1024)
- ORM packed (1024x1024)
- BEZ detail textura
- BEZ parallax-a
→ 3 texture samplea, 80 instrukcija
LOD 2 (daleko): Material_Simple
- Base Color (512x512)
- BEZ normal mape
- Roughness/Metallic kao konstante
→ 1 texture sample, 30 instrukcija
LOD 3 (najdalje): Material_Minimal
- Boja iz vertex color-a ili konstanta
- BEZ tekstura
→ 0 texture samplea, 15 instrukcija
Da biste ovo podesili u UE5:
- Otvorite Static Mesh Editor
- U LOD Settings, za svaki LOD nivo izaberite Material Slots
- Dodelite odgovarajuci materijal svakom LOD nivou
Automatic LOD Material Simplification
UE5 takodze podrzava automatsko pojednostavljivanje materijala za LOD-ove kroz Material Editor:
1. Feature Switches sa World Position Offset:
// U Material Editoru:
// Koristite "Distance to Camera" da automatski iskljucite feature-e
Distance to Camera → Divide (po MaxDistance) → Saturate →
Ako je distance > Threshold1: iskljuci detail textures
Ako je distance > Threshold2: iskljuci normal map
Ako je distance > Threshold3: iskljuci sve osim base color
Ali PAZNJA: ovo je losiji pristup od pravih LOD materijala, jer GPU i dalje kompajlira sve feature-e -- samo branch-uje oko njih. Pravi benefit dolazi od potpuno odvojenih materijala sa manje feature-a, gde kompajler moze da elimise nekorisni kod.
2. Material Function LOD Blending:
Kreirajte Material Function koja na osnovu rastojanja od kamere bira izmedju dva ulaza:
Input A (High Quality) ──┐
├── LOD Blend Function → Output
Input B (Low Quality) ──┘
↑
CameraDistance
Ovo je cist nacin da organizujete LOD logiku unutar jednog materijala.
Texture Resolution na Daljini
UE5 automatski koristi mipmap-e na osnovu rastojanja, tako da teksture na daljim objektima koriste manje mipmap nivoe (manje rezolucije). Ali mozete i eksplicitno kontrolisati texture streaming priority:
// Console komande za texture streaming
r.Streaming.PoolSize 1000 // Velicina texture streaming pool-a u MB
r.Streaming.FullyLoadedTextures // Lista tekstura koje se uvek ucitavaju u punoj rezoluciji
r.Streaming.MipBias 0 // Globalni mip bias (vece = nize rezolucije)
Za specifican materijal, mozete koristiti Texture Object sa podesenim MipValueMode da forsirate nizi mip nivo na daljini.
Feature Stripping na Daljini
Ovo je lista feature-a po prioritetu uklanjanja (prvi se uklanja na najvecoj daljini, poslednji se cuva najduze):
Prioritet uklanjanja (prvi se uklanja):
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. Parallax Occlusion Mapping -- nevidljiv na daljini, veoma skup
2. Detail Normal Maps -- nevidljive na daljini
3. Detail Textures (tiling) -- nevidljive na daljini
4. Subsurface Scattering -- efekat je suptilan na daljini
5. Clear Coat -- jedva vidljiv na daljini
6. Height-based Blending -- zameni sa tezinskim blendingom
7. Emissive Map -- zameni sa konstantom ili ukloni
8. AO Map -- SSAO pokriva na daljini
9. Normal Map -- na dovoljnoj daljini nevazna
10. Roughness/Metallic Map -- zameni sa konstantama
UVEK zadrzi:
- Base Color (bar u nekoj formi)
- Osnovne PBR konstante
Implementacija u Praksi: LOD Material System
Evo kompletnog workflow-a za implementaciju shader LOD sistema u UE5 projektu:
Korak 1: Definisajte LOD nivoe
LOD Nivo Rastojanje (m) Screen Size Cilj Instrukcija
──────── ────────────── ─────────── ─────────────────
LOD 0 0 - 10 > 0.5 Do 250
LOD 1 10 - 30 0.2 - 0.5 Do 120
LOD 2 30 - 100 0.05 - 0.2 Do 60
LOD 3 100+ < 0.05 Do 30
Korak 2: Kreirajte Material Instance-ove
Koristite Material Parameter Collections i Static Switches da kreirate varijante materijala:
Master Material:
Static Switch: "Use Normal Map"
Static Switch: "Use Detail Textures"
Static Switch: "Use Parallax"
Static Switch: "Use Emissive"
Material Instance LOD0: sve ON
Material Instance LOD1: Parallax OFF, Detail OFF
Material Instance LOD2: + Normal Map OFF, Emissive OFF
Material Instance LOD3: samo Base Color
Korak 3: Dodelite materijale LOD-ovima
U Static Mesh Editoru ili u Blueprint-u, dodelite odgovarajuci Material Instance svakom LOD nivou.
21.8 Prakticni Primeri Optimizacije
Primer 1: Landscape Material -- Od Skupog do Optimizovanog
Landscape materijali su notorno skupi jer moraju da blend-uju vise layera na svakom pikselu. Hajde da analiziramo tipican skup landscape material i optimizujemo ga.
Pocetno stanje (NEOPTIMIZOVAN):
Landscape Material sa 4 layera:
Svaki layer ima:
- Base Color (2048x2048) 1 sample
- Normal Map (2048x2048) 1 sample
- Roughness (2048x2048, odvojena) 1 sample
- AO (2048x2048, odvojena) 1 sample
- Height Map (za blending) 1 sample
Per-layer: 5 samplea × 4 layera = 20 samplea
Weightmap: 1 sample (RGBA za 4 layera)
Macro Variation Texture: 1 sample
Distance-based Tiling Variation: + 4-8 samplea (per layer, na blizini)
UKUPNO: 22-30 texture samplea!
Instruction count: 400-600+
DIJAGNOZA: Definitivno TEX bound. Crvena/bela u Shader Complexity.
Optimizacija Korak 1: Channel Packing
Svaki layer sada ima:
- Base Color (2048x2048) 1 sample
- Normal Map (2048x2048) 1 sample
- ORM packed (R=AO, G=Rough, B=Height) 1 sample
Per-layer: 3 samplea × 4 layera = 12 samplea
Weightmap: 1 sample
Macro Variation: 1 sample
UKUPNO: 14 samplea
Usteda: 8-16 samplea! (36-53% manje)
Optimizacija Korak 2: Weight-based Layer Skipping
// Umesto da blend-ujemo SVI layeri za svaki piksel,
// preskocimo layere sa nultom tezinom
float4 weights = Texture2DSample(Weightmap, Sampler, UV);
float3 finalColor = float3(0,0,0);
float3 finalNormal = float3(0,0,1);
// Samo sampleujemo layer ako ima nenultu tezinu
// NAPOMENA: Ovo zahteva branch, ali je cesto koherentno
// jer su landscape layeri prostorno grupisani
if (weights.r > 0.001) {
// Sample Layer 0
finalColor += SampleLayer0(UV) * weights.r;
}
if (weights.g > 0.001) {
// Sample Layer 1
finalColor += SampleLayer1(UV) * weights.g;
}
// ... itd za ostale layere
Ovo moze da smanji efektivni broj samplea na 3-6 za vecinu piksela (jer vecina piksela koristi samo 1-2 layera), ali sa cenom branch instrukcija. Na landscape-u, ovo je obicno isplativo jer su layeri prostorno koherentni.
Optimizacija Korak 3: Runtime Virtual Texturing
Sa RVT:
- Svi layeri se "ispeku" u virtualnu teksturu
- Pixel shader cita samo iz virtualne teksture
RVT Texture: 1-2 samplea (base color + normal)
Weightmap: NE TREBA (vec ispeccena u RVT)
Per-layer: 0 samplea u runtime-u!
UKUPNO: 1-3 texture samplea!!
Instruction count: 40-80
Ovo je dramticna usteda, ali RVT ima svoje troskove:
- Potrebno je GPU vreme za "pecenje" virtualne teksture
- Koristi memoriju za page table i fizicke stranice
- Dinamicno osvetljenje moze zahtevati cesce azuriranje
- Kvalitet moze da opada na veoma bliskim rastojanjima
Optimizacija Korak 4: Distance-based Simplifikacija
Blizu (< 20m): Puni material sa detail teksturama
14 samplea, 200 instrukcija
Srednje (20-100m): Bez detail tekstura, macro variation samo
8 samplea, 120 instrukcija
Daleko (> 100m): RVT sa pre-baked rezultatom
2 samplea, 50 instrukcija
Finalni rezultat:
| Metrika | Pre | Posle | Usteda |
|---|---|---|---|
| Max texture samples | 22-30 | 2-14 (zavisno od daljine) | 50-90%! |
| Max instruction count | 400-600 | 50-200 (zavisno od daljine) | 50-90%! |
| Shader Complexity boja | Crvena/Bela | Zelena/Zuta | Drasticno bolje |
Primer 2: Character Skin Material -- Od Kompleksnog do Mobile-Friendly
Skin (koza) materijali su posebno zahtevni jer zahtevaju Subsurface Scattering (SSS) da bi izgledali realisticno.
Pocetno stanje (HIGH-END PC verzija):
Character Skin Material:
Teksture:
- Base Color (4096x4096) 1 sample
- Normal Map (4096x4096) 1 sample
- Roughness Map (2048x2048) 1 sample
- Subsurface Color (2048x2048) 1 sample
- Subsurface Opacity/Thickness (2048x2048) 1 sample
- Specular Map (2048x2048) 1 sample
- Cavity/AO Map (2048x2048) 1 sample
- Detail Normal (1024x1024) 1 sample
- Pore Detail Mask (1024x1024) 1 sample
- Makeup/Tattoo layer (opciono) 0-1 sample
Shading Model: Subsurface Profile
- Screen-space subsurface scattering pass
- Burley diffusion model
Features:
- Dual-lobe specular
- Microgeometry from detail normal
- Screen-space contact shadows
UKUPNO: 9-10 texture samplea
Instruction Count: 180-250 (pixel shader samo)
+ SSS post-process pass: dodatnih 50+ instrukcija po pikselu!
Mobile-Friendly Verzija (Optimizovana):
Korak 1: Smanjite broj tekstura sa channel packing
Teksture (optimizovane):
- Base Color (1024x1024) 1 sample
R: Red, G: Green, B: Blue
- Normal Map (1024x1024) 1 sample
R: Normal.X, G: Normal.Y
- Combined Map (1024x1024) 1 sample
R: AO/Cavity
G: Roughness
B: Subsurface Opacity
A: Specular variation
UKUPNO TEKSTURA: 3 samplea (usteda: 6-7 samplea!)
Korak 2: Zamenite Subsurface Scattering sa aproksimacijom
Pravi SSS zahteva screen-space post-process pass koji je veoma skup. Za mobile, koristite jeftinu aproksimaciju:
// JEFTINA SSS APROKSIMACIJA (Pre-Integrated Skin Shading)
// Umesto screen-space scattering,
// koristimo pre-integrated BRDF lookup teksturu
float NdotL = dot(Normal, LightDir);
float curvature = length(fwidth(Normal)); // koliko je zakrivljena povrsina
// LUT: X os = NdotL, Y os = curvature
// Pre-computed u LUT teksturi: kako SSS izgleda za dati ugao i zakrivljenost
float2 sssUV = float2(NdotL * 0.5 + 0.5, curvature);
float3 sssLighting = Texture2DSample(SSS_LUT, Sampler, sssUV).rgb;
// Rezultat izgleda slicno pravom SSS-u za vecinu slucajeva
// Cena: 1 dot + fwidth + 1 TEX (LUT) ≈ 15-20 ALU + 1 TEX
// vs pravi SSS: 50+ ALU + fullscreen post-process pass
Korak 3: Uklonite Detail Normal i Pore Detail
Na mobilnim uredjajima, ekran je manji i rezolucija niza. Detail normal map koji prikazuje pore koze je nevidljiv na malom ekranu. Uklonite ga.
Usteda: 2 samplea + ~15 ALU (tangent space blending)
Korak 4: Zamenite Dual-Lobe Specular sa Single-Lobe
// HIGH-END: Dual-lobe specular (dva GGX lobe-a)
float spec = GGX(Roughness1, NdotH) * weight1 +
GGX(Roughness2, NdotH) * weight2;
// Cena: ~30-40 ALU (dva GGX izracunavanja)
// MOBILE: Single-lobe specular
float spec = GGX(Roughness, NdotH);
// Cena: ~15-20 ALU
// Vizuelna razlika je minimalna na mobilnim ekranima
Korak 5: Koristite Unlit + Custom Lighting za najnize uredjaje
Za najslabije mobilne uredjaje, mozete potpuno izbeci PBR lighting i koristiti pre-baked lighting u teksturi:
Teksture:
- Baked Lighting Texture (512x512): 1 sample
(sadrzi base color * ambient + baked directional light)
- Opciono: 1D ramp za toon-style dodatno svetlo
Shading Model: Unlit
Instruction Count: 5-10!
Ovo je drasticno pojednostavljenje koje gubi mnogo vizuelnog kvaliteta, ali na 5-inchnom ekranu sa ogranicenim hardverom, razlika je prihvatljiva.
Rezultati optimizacije skin materijala:
| Metrika | High-End PC | Mobile (optimizovan) | Mobile (minimal) |
|---|---|---|---|
| Texture Samples | 9-10 | 3-4 | 1 |
| ALU Instructions | 180-250 | 60-80 | 5-10 |
| Shading Model | Subsurface Profile | Default Lit | Unlit |
| SSS | Screen-space | LUT aproksimacija | Baked |
| Detail Normal | Da | Ne | Ne |
| Specular | Dual-lobe GGX | Single-lobe GGX | Nema (baked) |
| Vizuelni kvalitet | Fotorealistican | Dobar | Prihvatljiv |
Primer 3: Kompletna Optimizaciona Sesija -- Workflow
Hajde da prodjemo kroz tipicnu optimizacionu sesiju korak po korak, kao da sedite za racunarom i optimizujete materijale u svom projektu.
Korak 1: Identifikujte problem
1. Pokrenite igru u PIE (Play in Editor)
2. Otvorite console: stat unit
Rezultat:
Frame: 22.3ms (44 FPS) -- cilj je 16.6ms (60 FPS)
Game: 3.2ms
Draw: 4.1ms
GPU: 18.7ms ← GPU je bottleneck!
3. Otvorite console: stat gpu
Rezultat:
BasePass: 12.4ms ← vecina GPU vremena je ovde!
Shadows: 2.1ms
Lights: 1.8ms
PostProcess: 2.4ms
BasePass je skup → problem su materijali/shaderi
Korak 2: Vizualizujte problem
4. Prebacite na Shader Complexity view (Alt+8)
Rezultat: Landscape je BELA, character je ZUTA,
ostalo je ZELENO
→ Landscape material je daleko najskuplji element!
5. Testirajte da li je TEX ili ALU bound:
Console: r.MipMapLODBias 8
Rezultat: GPU vreme padne na 14ms
→ Delimicno TEX bound!
Vratite: r.MipMapLODBias 0
Korak 3: Analizirajte najskuplji materijal
6. Otvorite Landscape material u Material Editoru
Statistika:
Base pass: 487 instrukcija
82 ALU, 38 TEX, 367 other
38 texture samplea! To je MNOGO.
7. Analizirajte strukturu materijala:
- 5 landscape layera × (BaseColor + Normal + Roughness + AO + Height)
- = 5 × 5 = 25 layer samplea
- + weightmap: 2 samplea (5 layera zahtevaju 2 RGBA teksture)
- + macro variation: 1 sample
- + distance blend: +10 samplea (detail na blizini)
- = 38 samplea ukupno
Korak 4: Primenite optimizacije
8. Channel packing: Spojite AO + Roughness + Height u ORM teksturu
Usteda: 2 samplea po layeru × 5 = 10 samplea
Novi total: 28 samplea → bolje, ali i dalje mnogo
9. Smanjite na 4 layera (uklonite najmanje koriseni)
Usteda: 3 samplea + nesto ALU za blending
Novi total: 25 samplea
10. Implementirajte weight-based skipping
Efektivni samplea: ~8-12 (vecina piksela = 1-2 layera)
11. Uklonite distance blend za layere koji nisu dominantni
Usteda: ~6-8 samplea na blizini
12. Razmatrajte RVT za daleke rastojanja
Na daljini: 2-3 samplea umesto 20+
Korak 5: Verifikujte rezultate
13. Ponovo pokrenite stat unit:
Frame: 14.8ms (67 FPS!) -- iznad cilja!
GPU: 11.2ms
14. Shader Complexity: Landscape je sada ZUTA umesto BELA
15. Verifikujte vizuelni kvalitet:
- Da li landscape i dalje izgleda dobro? DA
- Da li ima vidljivih artefakata? NE
- Da li su prelazi izmedju layera glatki? DA
OPTIMIZACIJA USPESNA!
Cheat Sheet: Brze Optimizacije
Evo liste "quick wins" -- optimizacija koje mozete primeniti brzo sa minimalnim rizikom od vizuelne degradacije:
┌─────────────────────────────────────────────────────────────────┐
│ BRZE SHADER OPTIMIZACIJE │
├─────────────────────────────────────────────────────────────────┤
│ │
│ TEKSTURE: │
│ □ Channel pack ORM (AO + Roughness + Metallic u jednu) │
│ □ Koristite konstantne vrednosti umesto tekstura gde mozete │
│ □ Smanjite nepotrebno velike teksture (4K → 2K gde se ne vidi) │
│ □ Uklonite detail textures na objektima daleko od kamere │
│ │
│ MATEMATIKA: │
│ □ Zamenite pow(x,2) sa x*x │
│ □ Zamenite pow(x,n) sa uzastopnim mnozenjem za male n │
│ □ Zamenite sin/cos sa aproksimacijama gde je moguce │
│ □ Ne normalizujte vektore koji vec jesu (ili skoro) jedinicni │
│ │
│ ARHITEKTURA: │
│ □ Prebacite UV kalkulacije u vertex shader │
│ □ Koristite Quality Switch za Low/Medium/High verzije │
│ □ Implementirajte shader LOD (prostiji materijali na daljini) │
│ □ Koristite Material Instances umesto kopija materijala │
│ │
│ LANDSCAPE SPECIFIČNO: │
│ □ Max 4 layera po komponenti │
│ □ Channel pack sve layer teksture │
│ □ Razmatrajte RVT za srednje/daleke rastojanje │
│ □ Uklonite height-based blending na daljini │
│ │
│ TRANSLUCENT/PARTICLE SPECIFIČNO: │
│ □ Koristite Unlit gde god mozete │
│ □ Minimizirajte overdraw │
│ □ Koristite "Responsive AA" (smanjuje sample count) │
│ □ Pojednostavite particle shader koliko god mozete │
│ │
└─────────────────────────────────────────────────────────────────┘
21.9 Napredne Teme
Shader Permutacije i Kompilaciono Vreme
Kada koristite Static Switches u materijalu, UE5 generise odvojenu shader permutaciju za svaku kombinaciju. Sa N static switch-eva, potencijalno imate 2^N permutacija!
Primer:
Static Switch: Use Normal Map (ON/OFF)
Static Switch: Use Emissive (ON/OFF)
Static Switch: Use Detail (ON/OFF)
Permutacije: 2 × 2 × 2 = 8 verzija shadera
Sa 10 static switch-eva: 2^10 = 1024 permutacija!
Svaka se kompajlira odvojeno → dugo kompilaciono vreme
Kako upravljati permutacijama:
- Ne preterujte sa Static Switch-evima
- Koristite Quality Switch (ima fiksiran broj permutacija)
- Grupisajte povezane feature-e u jedan switch gde ima smisla
- Koristite Feature Level Switch umesto Static Switch za platform-specificne razlike
Async Shader Compilation
UE5 moze da kompajlira shadere asinhrono, sto sprečava stuttering pri prvom koriscenju materijala. Ali ovo znaci da ce se pri prvom vidjenju materijala prikazati default (obicno sivi) materijal dok se pravi shader kompajlira.
// Ukljucite/iskljucite async kompilaciju:
r.ShaderPipelineCacheAsync 1 // asinhrona kompilacija (default)
r.ShaderPipelineCacheAsync 0 // sinhrona (moze da uzrokuje stuttering)
// Pipeline State Object (PSO) caching:
r.ShaderPipelineCache.Enabled 1 // kesiranje kompajliranih shadera
Za shipping buildove, koristite PSO Caching da pre-kompajlirate sve shader permutacije. Ovo eliminise runtime kompilaciju i stuttering.
Ocekivanja po Platformi
Razlicite platforme imaju razlicite "budzete" za shadere:
HIGH-END PC (RTX 3080+, 1440p@60fps):
Budget po pixel shaderu: ~300-400 instrukcija (opaque)
Budget texture samplea: ~20-25
Napomena: Na 4K rezoluciji, smanjite budzet za ~40%
MID-RANGE PC (GTX 1660, 1080p@60fps):
Budget po pixel shaderu: ~150-200 instrukcija
Budget texture samplea: ~12-16
KONZOLE (PS5/XSX, 4K@30fps ili 1080p@60fps):
Budget po pixel shaderu: ~200-300 instrukcija
Budget texture samplea: ~16-20
Napomena: Konzole imaju predvidiv hardver, lakse za optimizaciju
MOBILE (High-end, 1080p@30fps):
Budget po pixel shaderu: ~50-80 instrukcija
Budget texture samplea: ~4-8
MOBILE (Low-end, 720p@30fps):
Budget po pixel shaderu: ~20-40 instrukcija
Budget texture samplea: ~2-4
SWITCH (720p@30fps docked):
Budget po pixel shaderu: ~60-100 instrukcija
Budget texture samplea: ~6-10
Ovi budzeti su okvirni i zavise od kompleksnosti scene, broja objekata, overdraw-a, i mnogo drugih faktora. Ali daju vam startnu tacku za planiranje.
Half Precision (FP16)
Moderni mobilni GPU-ovi (i neki desktop GPU-ovi) mogu da rade sa half precision (16-bit) floating point brojevima umesto punog 32-bit precision-a. Half precision je obicno duplo brzi za ALU operacije.
// Full precision (FP32):
float3 color = baseColor * lightIntensity;
// Half precision (FP16):
half3 color = (half3)baseColor * (half)lightIntensity;
// Na mobilnim GPU-ovima: potencijalno 2x brze!
Kada koristiti half precision:
- Boje (0-1 opseg, 16-bit je vise nego dovoljno)
- UV koordinate (obicno dovoljno tacno)
- Normalizovani vektori
Kada NE koristiti half precision:
- World position (moze da izazove jittering na velikim scenama)
- UV koordinate sa velikim tilingom (gubitak preciznosti)
- Dubinski proracuni (z-buffer kalkulacije)
U UE5 Material Editoru, mozete kontrolisati preciznost nekih izracunavanja, ali engine uglavnom automatski odlucuje sta moze da bude half precision. Za custom HLSL, eksplicitno koristite half tip gde je to bezbedno.
Wave Intrinsics i Optimizacija na Nivou Warp-a
Napredna tehnika: koriscenje wave intrinsics (HLSL Shader Model 6.0+) za komunikaciju izmedju piksela unutar istog warp-a.
// Umesto da svaki piksel racuna prosek:
float avg = someExpensiveCalculation();
// Sa wave intrinsics, jedan piksel racuna i deli rezultat:
float avg = WaveReadLaneFirst(someExpensiveCalculation());
// Samo prvi lane racuna, ostali citaju rezultat
Ovo je napredna tehnika koja zahteva razumevanje GPU arhitekture (Poglavlje 08). UE5 koristi wave intrinsics interno za neke optimizacije (npr. u Nanite i Lumen sistemima), ali obicno ne u korisnickim materijalima.
21.10 Alati za Profajliranje Shadera
UE5 Ugradeni Alati
1. stat gpu
Console: stat gpu
Prikazuje: vreme po GPU pass-u
Koristi za: identifikaciju koji pass je najskuplji
2. stat unit
Console: stat unit
Prikazuje: ukupne timinge (Game, Draw, GPU)
Koristi za: identifikaciju da li je CPU ili GPU bottleneck
3. stat rhi
Console: stat rhi
Prikazuje: RHI statistike (draw calls, primitives, triangles)
Koristi za: identifikaciju prekomerne geometrije
4. ProfileGPU (Ctrl+Shift+,)
Shortcut: Ctrl+Shift+, (zarez)
Prikazuje: detaljni GPU profil sa vremenima po pass-u
Koristi za: najdetaljniji pregled GPU performansi u editoru
5. Shader Complexity Viewmode
Viewport: Optimization Viewmodes > Shader Complexity
Prikazuje: vizuelnu heat mapu instruction cost-a
Koristi za: brzu identifikaciju skupih materijala
6. Material Statistics
Material Editor: donji levi ugao
Prikazuje: instruction count, texture sample count
Koristi za: poredjenje razlicitih verzija istog materijala
Spoljni Alati
RenderDoc (Besplatan)
Koraci:
1. Skinite sa renderdoc.org
2. U UE5: Edit > Project Settings > Engine > Rendering
→ Omogucite "Render Doc Plugin"
3. Pritisnite F12 da snimite frame (ili Alt+F12)
4. RenderDoc se otvara sa snimljenim frame-om
5. Mozete da analizirate svaki draw call, vidite shadere,
meriti vreme po operaciji
NVIDIA Nsight Graphics
Najbolji alat za NVIDIA GPU-ove:
- Prikazuje ALU vs TEX utilizaciju
- Warp occupancy
- Cache hit/miss rate
- Detaljne metrike po shaderu
- Requires NVIDIA GPU
Koraci:
1. Instalirajte Nsight Graphics (besplatan za NVIDIA GPU)
2. Pokrenite UE5 kroz Nsight ("Launch" mode)
3. Snimite frame
4. Analizirajte shader metrike
PIX (Microsoft)
Za Windows i Xbox development:
- Detaljni GPU timings
- Shader debugging
- Pipeline state analiza
- Resource viewer
Koraci:
1. Skinite PIX sa Microsoft sajta
2. Pokrenite UE5 pod PIX-om
3. Snimite frame (GPU Capture)
4. Analizirajte u PIX interfejsu
AMD Radeon GPU Profiler (RGP)
Za AMD GPU-ove:
- Wavefront occupancy
- Cache utilizacija
- Instruction timing
- Pipeline bottleneck analiza
Prakticni Saveti za Profajliranje
1. UVEK profajlirajte na CILJNOM hardveru
- Profajliranje na RTX 4090 vam nece pomoci da
optimizujete za GTX 1650
2. Profajlirajte u SHIPPING ili DEVELOPMENT buildu
- Editor ima overhead koji moze da zamaskira prave probleme
3. Iskljucite V-SYNC pri profajliranju
- V-Sync limitira framerate i sakriva pravu GPU cenu
Console: r.VSync 0
4. Koristite FIKSIRANU scenu za poredjenje
- Snimite tacnu poziciju kamere
- Koristite istu scenu pre i posle optimizacije
5. Merite VISE puta i uzmite prosek
- GPU timings variraju od frame-a do frame-a
- Uzmite prosek od 100+ frejmova za pouzdane rezultate
6. Optimizujte NAJSKUPLJE stvari prvo
- Nemojte gubiti vreme na optimizaciju shadera koji
zauzima 0.1ms kada imate shader koji zauzima 5ms
21.11 Rezime i Kljucni Pojmovi
Kljucne Lekcije
- Instruction count nije sve -- tip instrukcija i memory access patern su jednako vazni
- Channel packing je "besplatna" optimizacija -- uvek radite, nema vizuelnog gubitka
- Identifikujte bottleneck pre optimizacije -- ALU bound vs TEX bound zahtevaju razlicite pristupe
- Shader LOD je kriticno vazan -- ne koristite isti materijal na svim rastojanjima
- Profajlirajte na ciljnom hardveru -- GPU profiler je vas najbolji prijatelj
- Dependent texture reads su tihi ubica -- pazite na lanac zavisnosti u texture samplima
- Translucent materijali traze posebnu paznju -- overdraw mnozbeno povecava cenu
Tabela Kljucnih Pojmova
| Pojam (EN) | Opis |
|---|---|
| Instruction Count | Broj instrukcija u kompajliranom shaderu |
| ALU Instruction | Aritmeticko-logicka instrukcija (matematika) |
| TEX Instruction | Instrukcija za citanje iz teksture |
| ALU Bound | Stanje gde su compute jedinice GPU-a bottleneck |
| TEX Bound | Stanje gde je memorijski bandwidth bottleneck |
| Shader Complexity | UE5 vizualizacioni rezim koji prikazuje cenu shadera |
| Dependent Texture Read | Texture sample ciji UV zavisi od rezultata prethodnog samplea |
| Channel Packing | Pakovanje vise grayscale mapa u kanale jedne teksture |
| Texture Atlas | Velika tekstura koja sadrzi vise manjih tekstura |
| LUT (Lookup Table) | Tekstura koja sadrzi preracunate vrednosti kompleksnih funkcija |
| Shader LOD | Koriscenje prostijih shadera za objekte na vecoj daljini |
| Transcendental Function | Matematicka funkcija (sin, cos, pow, exp, log) koja je skuplja od osnovne aritmetike |
| Overdraw | Renderovanje istog piksela vise puta (zbog preklapanja objekata) |
| Shader Permutation | Varijanta shadera generisana za specificnu kombinaciju feature-a |
| Quality Switch | UE5 node koji bira razlicite puteve na osnovu quality level-a |
| RVT (Runtime Virtual Texturing) | UE5 sistem koji "pece" kompleksne materijale u virtualne teksture |
| Half Precision (FP16) | 16-bitni floating point, brzi ali manje precizan od FP32 |
| Warp/Wavefront | Grupa piksela (obicno 32 ili 64) koja se izvrsava zajedno na GPU |
| PSO Cache | Pre-kompajlirani shader cache za eliminaciju runtime kompilacije |
| Interpolator/Varying | Podatak koji se prenosi iz vertex u pixel shader |
| MAD (Multiply-Add) | Operacija a*b+c izvedena kao jedna instrukcija |
| Cache Hit/Miss | Da li su trazeni podaci vec u brzom kesu GPU-a |
| Mipmap | Niz sve manjih verzija teksture za razlicita rastojanja |
| Anisotropic Filtering | Filtriranje teksture koje poboljsava kvalitet pod uglom |
21.12 Reference i Dalje Citanje
Poglavlja u Ovoj Knjizi
- Poglavlje 08: GPU Arhitektura -- Kako GPU zapravo izvrsava shadere, warp-ovi, SIMD, memorijski podsistem
- Poglavlje 17: Shaderi i Branching -- Detaljno o shader jezicima, branching-u, i flow control-u
- Poglavlje 22: UE5 Material System -- Prakticno koriscenje Material Editora, Material Instances, Material Functions
Zvanicna Dokumentacija
- UE5 Material Optimization Guide: docs.unrealengine.com/5.0/en-US/material-optimization/
- UE5 Performance Guidelines: docs.unrealengine.com/5.0/en-US/performance-guidelines-for-unreal-engine/
- UE5 Shader Development: docs.unrealengine.com/5.0/en-US/shader-development-in-unreal-engine/
- UE5 Virtual Texturing: docs.unrealengine.com/5.0/en-US/runtime-virtual-texturing-in-unreal-engine/
GPU Proizvodjaci -- Best Practices
- NVIDIA GPU Best Practices: developer.nvidia.com/gpu-gems
- AMD GPU Open: gpuopen.com
- ARM Mali GPU Best Practices: developer.arm.com/documentation/102643/latest
- Qualcomm Adreno GPU: developer.qualcomm.com/software/adreno-gpu-sdk
Prezentacije i Clanci
- "Optimizing the Graphics Pipeline with Compute" (GDC): Objasnjava kako compute shaderi mogu da zamene tradicionalne pipeline faze
- "Practical Real-Time Strategies for Accurate Indirect Occlusion" (SIGGRAPH): Napredne AO tehnike i njihova cena
- "Real-Time Rendering, 4th Edition" (Tomas Akenine-Moller et al.): Biblija realtime renderinga -- poglavlja o shader optimizaciji su odlicna
- "GPU Gems" serija (NVIDIA): Besplatno dostupna online, puna prakticnih tehnika
Alati
- RenderDoc: renderdoc.org -- Besplatan GPU debugger i profiler
- NVIDIA Nsight Graphics: developer.nvidia.com/nsight-graphics -- GPU profiler za NVIDIA
- PIX: devblogs.microsoft.com/pix/ -- Microsoft GPU profiler
- AMD Radeon GPU Profiler: gpuopen.com/rgp/ -- GPU profiler za AMD
Sledece poglavlje (22): UE5 Material System -- Sada kada znate kako da merite i optimizujete shadere, naucicemo kako da efikasno koristite UE5 Material Editor, Material Instances, Material Functions, i sve alate koje vam engine pruza za kreiranje materijala koji izgledaju odlicno I rade brzo.
Zapamtite: optimizacija nije neprijatelj lepote. Optimizovan shader koji radi na 60 FPS je lepsi od fotorealisticnog shadera koji radi na 15 FPS. Vas cilj je da postignete najbolji moguchi vizuelni kvalitet u okviru budzeta koji vam hardver dozvoljava.