Poglavlje 49: CPU Optimizacija

Poglavlje 49: CPU Optimizacija

U ovom poglavlju ulazimo duboko u svet CPU optimizacije u Unreal Engine 5. Naucicete kako da identifikujete, razumete i resite svaki znacajan CPU bottleneck -- od game thread-a koji gusi logiku igre, preko render thread-a koji ne stize da pripremi draw call-ove, do skrivenih ubica kao sto su Garbage Collection pauze, Blueprint overhead i previse aktora koji tikuju svaki frejm. Ovo nije poglavlje o teoriji -- ovo je poglavlje koje ce vam sacuvati milisekunde, a svaka milisekunda se racuna.


Uvod: CPU -- Nevidljivi Dirigent

Zamislite orkestar. GPU je zvucnik -- on proizvodi finalnu sliku, piksele na ekranu. Ali CPU? CPU je dirigent. On odlucuje sta se svira, kada se svira, i kojim redosledom se svira. Ako dirigent ne stize -- ako zamuckuje, kasni sa uputstvima, ili se zagubi u notama -- ceo orkestar staje. Nije bitno koliko je zvucnik mocan ako nema sta da pusti.

U Unreal Engine 5, CPU radi ogromnu kolicinu posla koja je potpuno nevidljiva igracu:

Sve ovo mora da se zavrsi unutar jednog frame-a. Na 60 FPS, to je 16.67 milisekundi. Na 30 FPS -- 33.33 milisekundi. Zvuci puno? Verujte mi, nije.

Ako ste procitali poglavlje 40 (UE5 Profiling Alati), vec znate kako da identifikujete da li je CPU vas bottleneck -- stat unit komanda jasno pokazuje Game thread, Draw (Render) thread i GPU vremena. Ako ste procitali poglavlje 39 (Optimizacija -- Filozofija), znate da nikada ne treba optimizovati naslepo. U ovom poglavlju pretpostavljamo da ste uradili profajliranje, ustanovili da je CPU problem, i sada trazite konkretna resenja.

Krenimo od temelja.


49.1 Game Thread vs Render Thread: Dva Mozga Jednog Engine-a

49.1.1 Sta radi Game Thread?

Game thread (ponekad nazvan "main thread" ili prikazan kao "Game" u stat unit) je nit koja izvrsava svu logiku vase igre. Svaki frejm, game thread radi sledece, ovim pribliznim redosledom:

  1. Input processing -- cita ulaz sa tastature, misa, gamepad-a
  2. Actor Tick -- poziva Tick() funkciju na svakom aktoru koji ima omogucen ticking
  3. Component Tick -- poziva Tick() na svakoj komponenti koja ima omogucen ticking
  4. Physics simulation -- pokrece Chaos physics solver
  5. Animation evaluation -- evaluira AnimGraph, skeletal mesh-ove, blend space-ove
  6. AI update -- Behavior Tree-ovi, EQS upiti, AI Perception
  7. Navigation -- NavMesh upiti, path finding
  8. Gameplay framework -- GameMode, GameState, PlayerController logika
  9. Blueprint execution -- sav Blueprint kod koji se izvrsava ovog frejma
  10. Garbage Collection -- periodocno ciscenje memorije
  11. Async task completion -- zavrsavanje asinhronih operacija

Sve ovo je sekvencijalno unutar game thread-a (sa nekim izuzecima gde engine koristi paralelizaciju, o cemu cemo pricati u sekciji 49.7). Ako imate aktor sa skupim Tick-om koji traje 5 ms, i jos 200 aktora koji tikuju po 0.05 ms svaki -- to je 5 + 10 = 15 ms samo na ticking. Na 60 FPS, skoro vas ceo budzet je potrosen samo na tikovanje aktora.

49.1.2 Sta radi Render Thread?

Render thread (prikazan kao "Draw" u stat unit) je potpuno odvojena nit od game thread-a. Njen posao je da pripremi sve sto GPU treba da nacrta:

  1. Scene proxy update -- azurira render-side predstave (proxy-je) objekata na osnovu promena iz game thread-a
  2. Visibility determination -- prolazi kroz scenu i odlucuje koji objekti su potencijalno vidljivi
  3. Occlusion culling -- preciznije testira da li su objekti zaista vidljivi ili ih nesto zaklanja
  4. Draw call preparation -- za svaki vidljivi objekat, priprema mesh draw command sa svim potrebnim state-ovima
  5. Sorting -- sortira transparentne objekte po dubini, sortira opaque objekte po materijalu za bolji batching
  6. Light setup -- priprema podatke o svetlima koja uticu na scenu
  7. Shadow setup -- odlucuje koji objekti bacaju senke i u koje shadow mape
  8. Command list building -- pakuje sve komande u command buffer koji ce GPU izvrsiti

Kljucna stvar: render thread ne crta nista na ekranu. On samo priprema instrukcije za GPU. Ali ta priprema moze biti vrlo skupa -- pogotovo ako imate mnogo objekata, mnogo svetala, ili kompleksnu scenu.

49.1.3 Kako rade paralelno -- Pipeline Model

Ovo je mozda najvazniji koncept za razumevanje CPU performansi u UE5. Game thread i render thread rade paralelno, ali sa jednim frame-om razmaka:

Game Thread:   [Frame N+1][Frame N+2][Frame N+3][Frame N+4] ...
Render Thread: [         ][Frame N  ][Frame N+1][Frame N+2] ...
GPU:           [         ][         ][Frame N  ][Frame N+1] ...

Vreme ---->

Dok game thread racuna logiku za frame N+1, render thread priprema rendering za frame N (koji je game thread vec zavrsio), a GPU renderuje frame N-1. Ovo je pipelined execution -- svaka faza radi na razlicitom frame-u istovremeno.

Zasto je ovo vazno? Zato sto se frame time odredjuje najsporijim od ova tri, a ne njihovim zbirom. Ako game thread traje 8 ms, render thread 6 ms, a GPU 12 ms, vas frame time ce biti ~12 ms (plus mali overhead za sinhronizaciju), a ne 26 ms.

Ali ovo takodje znaci da ako je jedan thread dramaticno sporiji od ostalih, ostali ce cekati:

Scenario: Game Thread je bottleneck

Game Thread:   [████████████████████████]  20 ms
Render Thread: [████████░░░░░░░░░░░░░░░░]  8 ms (ceka game thread)
GPU:           [██████████░░░░░░░░░░░░░░]  10 ms (ceka render thread)

Frame Time = 20 ms (50 FPS umesto 60 FPS)
Scenario: Render Thread je bottleneck

Game Thread:   [██████░░░░░░░░░░░░░░░░░░]  6 ms (zavrsava brzo, ali...)
Render Thread: [████████████████████████]  24 ms (spora priprema scene)
GPU:           [████████████░░░░░░░░░░░░]  12 ms (ceka render thread)

Frame Time = 24 ms (~41 FPS)

49.1.4 Game Thread moze da blokira Render Thread (i obrnuto)

Pored osnovnog pipeline modela, postoje situacije gde jedan thread eksplicitno ceka drugi:

Game thread ceka render thread:

Render thread ceka game thread:

FlushRenderingCommands:

// LOSE: Ovo ubija paralelizam
FlushRenderingCommands();
// Game thread je pauziran dok render thread ne zavrsi SVE

// BOLJE: Koristite async verzije operacija kad god je moguce
// Vecina UE5 operacija ima async alternativu

49.1.5 RHI Thread -- Treci Igrac

Pored game i render thread-a, UE5 ima i RHI (Rendering Hardware Interface) thread. Ovaj thread je posrednik izmedju render thread-a i grafickog API-ja (DirectX 12, Vulkan). Njegov posao je da prevodi engine-level rendering komande u API-specificne pozive.

RHI thread je retko bottleneck u modernim projektima, ali moze postati problem ako imate:

U stat unit, RHI thread se prikazuje kao RHIT. Ako vidite da je RHIT veci od Draw i Game, imate problem na RHI nivou -- sto je neobicno i obicno ukazuje na nesto fundamentalno pogresno sa rendering pipeline-om ili drajverom.


49.2 Tick Budgeting: Kontrola Nad Kostom Svakog Frejma

49.2.1 Problem: Svaki Tick Kosta

Svaki aktor i svaka komponenta u UE5 ima mogucnost da "tikuje" -- da izvrsava kod svaki frejm. Ovo je osnova gameplay programiranja: proveravate input, pomerate aktore, proveravate uslove, azurirate stanja.

Ali hajde da napravimo racunicu:

Scena sa 500 aktora, svaki tikuje
Prosecna cena jednog Tick-a: 0.02 ms
Ukupno: 500 * 0.02 ms = 10 ms

Na 60 FPS (budzet 16.67 ms), potrosili ste 60% budzeta
samo na tikovanje -- pre fizike, animacija, AI-a, renderinga...

Zvuci malo, 0.02 ms po aktoru? Da, svaki individualni tick je jeftin. Ali skaliranje je ubica. Ovo je jedan od najcescih CPU bottleneck-ova u UE5 projektima, i jedan od najlaksih za resavanje.

49.2.2 Smanjenje frekvencije tikovanja

Prva i najjednostavnija optimizacija: ne mora svaki aktor da tikuje svaki frejm.

Tick Interval:

// U konstruktoru aktora
AMyActor::AMyActor()
{
    PrimaryActorTick.bCanEverTick = true;
    PrimaryActorTick.TickInterval = 0.1f; // Tikuj samo svakih 100ms (10 puta u sekundi)
}
// Ili dinamicki, u runtime-u
SetActorTickInterval(0.5f); // Tikuj dva puta u sekundi

U Blueprintu, ovo mozete podesiti u Class Defaults:

Actor Tick > Tick Interval: 0.1

Kada koristiti smanjenu frekvenciju:

Tip logike Preporuceni interval
Movement (igrac, neprijatelji) Svaki frejm (0.0)
AI odlucivanje 0.1 - 0.5 sekundi
UI update 0.05 - 0.2 sekundi
Proveravanje uslova (da li je igrac blizu?) 0.2 - 1.0 sekundi
Ambient efekti (rotirajuci objekat) 0.033 (30 Hz)
Proveravanje inventara 0.5 - 2.0 sekundi
Daleki NPC-ovi 0.5 - 1.0 sekundi

Vazno: Kada koristite Tick Interval, vasa Tick funkcija prima DeltaTime koji odrazava stvarno proteklo vreme od poslednjeg tick-a tog aktora -- ne globalni frame delta. To znaci da vam logika zasnovana na DeltaTime i dalje radi korektno, samo se izvrsava redje.

49.2.3 Potpuno iskljucivanje tick-a

Najbolji tick je onaj koji ne postoji:

// U konstruktoru -- nikada ne tikuj
AMyActor::AMyActor()
{
    PrimaryActorTick.bCanEverTick = false;
}

Mnogi aktori uopste ne treba da tikuju. Primeri:

Pravilo palca: Ako vas aktor ne radi nista aktivno svaki frejm, iskljucite mu tick. Koristite event-driven pristup umesto toga -- overlap events, delegate, timer-e.

49.2.4 Dinamicko ukljucivanje/iskljucivanje tick-a

Cesto zelite da aktor tikuje samo kada je relevantan -- na primer, kada je igrac blizu:

// Iskljuci tick (runtime)
SetActorTickEnabled(false);

// Ukljuci tick (runtime)
SetActorTickEnabled(true);

Prakticni pattern za distance-based tick management:

void AMyActor::CheckShouldTick()
{
    APawn* PlayerPawn = UGameplayStatics::GetPlayerPawn(this, 0);
    if (!PlayerPawn) return;
    
    float Distance = FVector::Dist(GetActorLocation(), PlayerPawn->GetActorLocation());
    
    if (Distance < ActivationDistance && !IsActorTickEnabled())
    {
        SetActorTickEnabled(true);
    }
    else if (Distance > DeactivationDistance && IsActorTickEnabled())
    {
        SetActorTickEnabled(false);
    }
}

Ali cekajte -- ako ste iskljucili tick, kako cete proveravati distancu? Resenje:

  1. Timer-based provera: Koristite SetTimer da povremeno proveravate distancu
  2. Significance Manager: UE5 ima ugradjeni sistem (vise o njemu uskoro)
  3. Centralizovani manager: Jedan "manager" aktor tikuje i proverava distance za sve aktore

49.2.5 Timer-Based Updates umesto Tick-a

Timer-i su cesto bolji od Tick-a jer:

// Postavi timer koji poziva funkciju svakih 0.5 sekundi
GetWorldTimerManager().SetTimer(
    MyTimerHandle,         // Handle za kasniju kontrolu
    this,                  // Objekat
    &AMyActor::DoUpdate,   // Funkcija koja se poziva
    0.5f,                  // Interval u sekundama
    true                   // Da li se ponavlja
);

// Zaustavi timer
GetWorldTimerManager().ClearTimer(MyTimerHandle);

U Blueprintu:

Set Timer by Function Name
    Function Name: "DoUpdate"
    Time: 0.5
    Looping: true

Timer vs Tick -- Kada koristiti koji:

Situacija Preporuka
Mora da se izvrsava svaki frejm (movement, input) Tick
Mora da se izvrsava sa fiksnim intervalom Timer
Povremena provera stanja Timer
Event koji se desava jednom nakon delay-a Timer (non-looping)
Potrebna precizna sinhronizacija sa rendering-om Tick

49.2.6 Significance Manager

UE5 ima ugradjeni Significance Manager sistem koji automatski upravlja znacajnoscu objekata na osnovu njihove udaljenosti od kamere i drugih faktora.

// Registrujte aktor sa Significance Manager-om
USignificanceManager* SM = FSignificanceManagerModule::Get(GetWorld());
if (SM)
{
    SM->RegisterObject(
        this,                              // Objekat
        TEXT("MyActorTag"),                // Tag
        [](USignificanceManager::FManagedObjectInfo* Info, 
           const FTransform& ViewPoint) -> float
        {
            // Funkcija koja racuna "znacajnost" -- veca vrednost = bitniji objekat
            float Distance = FVector::Dist(
                Info->GetObject()->GetActorLocation(), 
                ViewPoint.GetLocation()
            );
            
            // Blize = znacajnije (inverzan odnos sa distancom)
            return FMath::Max(0.f, 1.f - Distance / 10000.f);
        },
        USignificanceManager::EPostSignificanceType::Sequential
    );
}

Na osnovu vraceene znacajnosti, mozete prilagoditi ponasanje aktora:

void AMyActor::OnSignificanceChanged(float OldSignificance, float NewSignificance)
{
    if (NewSignificance > 0.8f)
    {
        // Blizu igraca -- pun kvalitet
        SetActorTickInterval(0.0f);  // Svaki frejm
        SkeletalMesh->SetAnimationMode(EAnimationMode::AnimationBlueprint);
    }
    else if (NewSignificance > 0.3f)
    {
        // Srednja udaljenost -- smanjen kvalitet
        SetActorTickInterval(0.1f);  // 10 Hz
        SkeletalMesh->SetAnimationMode(EAnimationMode::AnimationBlueprint);
    }
    else
    {
        // Daleko -- minimalan kvalitet
        SetActorTickInterval(0.5f);  // 2 Hz
        SkeletalMesh->SetAnimationMode(EAnimationMode::AnimationCustomMode);
        // Ili cak potpuno zaustavite animacije
    }
}

Fortnite intenzivno koristi Significance Manager za upravljanje stotinama igraca i NPC-ova. Aktori koji su daleko od kamere dobijaju smanjenu frekvenciju tick-a, simplifikovane animacije, i manje preciznu fiziku.

49.2.7 Tick Groups i Tick Dependencies

UE5 ima koncept tick grupa koje odredjuju redosled izvrsavanja tick-ova:

TG_PrePhysics      -- Pre fizicke simulacije
TG_StartPhysics    -- Pocetak fizike
TG_DuringPhysics   -- Tokom fizike (paralelno sa fizikom!)
TG_EndPhysics      -- Kraj fizike
TG_PostPhysics     -- Posle fizicke simulacije
TG_PostUpdateWork  -- Posle svega

Ovo je vazno za optimizaciju jer mozete:

  1. Staviti tick u TG_DuringPhysics da bi se izvrsavao paralelno sa fizickom simulacijom (ako ne zavisi od rezultata fizike)
  2. Pravilno podesiti zavisnosti da izbegnete nepotrebno cekanje
// Stavite tick u grupu koja se izvrsava tokom fizike
PrimaryActorTick.TickGroup = TG_DuringPhysics;

Upozorenje: Budite pazljivi sa TG_DuringPhysics -- ako vas tick pristupa fizickim podacima koji se upravo azuriraju, dobicete nedeterministicko ponasanje (race conditions).


49.3 Component Optimization: Manje je Vise

49.3.1 Svaka Komponenta Kosta

Aktori u UE5 su kontejneri za komponente. Svaka komponenta koju dodate na aktor ima svoju cenu:

Hajde da pogledamo tipicne cene nekih komponenti:

Komponenta Tick cena Memorija Rendering overhead
StaticMeshComponent Nizak (ne tikuje podrazumevano) Srednja Draw call
SkeletalMeshComponent Visok (evaluira animacije) Velika Draw call + bone transforms
CharacterMovementComponent Visok Srednja Nikakav
AudioComponent Srednji Mala Nikakav
ParticleSystemComponent Visok Velika Visestruki draw calls
BoxCollisionComponent Nizak Mala Nikakav (debug only)
AIPerceptionComponent Srednji-Visok Srednja Nikakav
WidgetComponent Visok (UI update) Velika Draw call
SplineComponent Nizak Srednja Debug only
SceneComponent Minimalan Mala Nikakav

49.3.2 Deaktivacija nekoristenih komponenti

Ne morate unistiti komponentu da biste eliminisali njen overhead -- mozete je samo deaktivirati:

// Deaktiviraj komponentu -- zaustavlja tick, uklanja iz scene queries
MyComponent->Deactivate();

// Reaktiviraj kada bude potrebna
MyComponent->Activate();

Za rendering komponente (mesh-eve), koristite visibility:

// Sakrij mesh -- uklanja ga iz rendering-a ali komponenta ostaje
MyMeshComponent->SetVisibility(false);

// Ili jos bolje, potpuno ga ukloni iz scene:
MyMeshComponent->SetHiddenInGame(true);

Razlika izmedju SetVisibility(false) i Deactivate():

Za Skeletal Mesh komponente, imate dodatne opcije:

// Zaustavi evaluaciju animacija (ali komponenta ostaje aktivna)
SkeletalMeshComp->SetComponentTickEnabled(false);

// Ili koristi URO (Update Rate Optimization) -- vise o ovome uskoro
SkeletalMeshComp->VisibilityBasedAnimTickOption = EVisibilityBasedAnimTickOption::OnlyTickPoseWhenRendered;

49.3.3 Biranje laksih alternativa

Ponekad pravi odgovor nije optimizacija postojece komponente vec zamena sa laksom alternativom:

Static Mesh umesto Skeletal Mesh: Ako objekat nema animacije, koristite Static Mesh. Skeletal Mesh evaluira bone transformacije svaki frejm cak i bez AnimBP-a, sto je nepotreban overhead. Izuzetak je ako vam treba morphing ili proceduralna animacija -- ali i tada razmislite da li Static Mesh sa Material-based animacijom moze da posluzi (npr. World Position Offset u materijalu za vetar koji duva travu).

InstancedStaticMeshComponent umesto mnogo StaticMeshComponent-i: Ako imate 100 istih mesh-eva (npr. drve u sumi), jedan InstancedStaticMeshComponent (ili HierarchicalInstancedStaticMeshComponent) je drasticno jeftiniji od 100 odvojenih StaticMeshComponent-i.

// LOSE: 100 odvojenih aktora sa StaticMeshComponent
for (int i = 0; i < 100; i++)
{
    SpawnActor<ATreeActor>(Location);
    // 100 aktora, 100 komponenti, 100 tick registracija, 100 draw call-ova
}

// BOLJE: Jedan aktor sa HISM komponentom
UHISM = CreateDefaultSubobject<UHierarchicalInstancedStaticMeshComponent>(TEXT("Trees"));
for (int i = 0; i < 100; i++)
{
    HISM->AddInstance(TransformForTree[i]);
    // 1 aktor, 1 komponenta, 1 tick registracija, MNOGO manje draw call-ova
}

Billboard umesto mesh-a za daleke objekte: Za objekte koji su daleko i koji nikada nece biti blizu kamere, razmislite o Billboard komponenti ili imposter sprite-ovima.

49.3.4 Component Tick optimizacija

Svaka komponenta ima nezavisnu kontrolu nad ticking-om:

// Iskljuci tick na komponenti (nezavisno od aktora)
MyComponent->SetComponentTickEnabled(false);

// Postavi tick interval na komponenti
MyComponent->SetComponentTickInterval(0.5f);

Cesta greska: ostavljanje tick-a ukljucenog na komponentama koje ne rade nista u Tick-u. UE5 ce i dalje trositi overhead na pozivanje prazne Tick funkcije, registrovanje u tick sistemu, i pracenje tick zavisnosti.

Auditiranje tick-ova u vasem projektu:

Koristite stat dumpticks konzolnu komandu da vidite sve aktore i komponente koje tikuju:

dumpticks           // Stampa sve tickove u log
stat tickgroups     // Prikazuje statistiku po tick grupama

Ovo je jedan od najkorisnijih debugging alata za CPU optimizaciju. Cesto cete otkriti da imate stotine aktora koji tikuju a da vi niste ni svesni -- jer su spawn-ovani od strane nekog sistema, ili jer neka komponenta ima tick ukljucen podrazumevano.


49.4 Actor Lifecycle Management: Spawn, Destroy, Pool

49.4.1 Cena Spawn-ovanja i Destroy-ovanja

Svaki put kada pozovete SpawnActor(), UE5 radi sledece:

  1. Alocira memoriju za novi UObject
  2. Konstruise objekat -- poziva konstruktor, inicijalizuje default vrednosti
  3. Registruje na scenu -- dodaje aktor u scene octree, registruje komponente
  4. Poziva BeginPlay -- izvrsava svu inicijalizacionu logiku
  5. Registruje za ticking -- dodaje aktor u tick listu (ako ima tick)
  6. Registruje za repliciranje -- ako je multiplayer, registruje za mrenu repliciju

Svaki put kada pozovete DestroyActor():

  1. Poziva EndPlay -- izvrsava cleanup logiku
  2. Deregistruje sa scene -- uklanja iz octree-a, deregistruje komponente
  3. Deregistruje tick -- uklanja iz tick liste
  4. Markira za Garbage Collection -- objekat se ne brise odmah, vec se markira za GC

Ove operacije nisu besplatne. Za jednostavan aktor, SpawnActor moze da traje 0.05-0.2 ms. Za kompleksan aktor sa mnogo komponenti, moze da traje 1-5 ms. DestroyActor je obicno brzi, ali Garbage Collection koja sledi moze da izazove nepredvidive pauze.

Kada ovo postaje problem?

Zamislite pucacinu. Svaki put kada igrac puca, spawn-ujete metak (Actor), spawn-ujete particle efekt za bljesak (Actor), i spawn-ujete zvuk (Actor). Igrac puca 10 puta u sekundi. To je 30 spawn-ova i 30 destroy-a u sekundi. Za jednog igraca. U multiplayer-u sa 8 igraca? To je potencijalno 240 spawn-ova i 240 destroy-a u sekundi. CPU plače.

49.4.2 Object Pooling -- Reciklirajte Umesto Da Bacate

Object pooling je tehnika gde unapred kreirate grupu objekata, i umesto da ih unistavate kada vise nisu potrebni, "vracate" ih u bazen (pool) za ponovnu upotrebu.

// Jednostavan Object Pool
UCLASS()
class AProjectilePool : public AActor
{
    GENERATED_BODY()
    
public:
    void InitializePool(TSubclassOf<AProjectile> ProjectileClass, int32 PoolSize);
    AProjectile* GetProjectile();
    void ReturnProjectile(AProjectile* Projectile);
    
private:
    UPROPERTY()
    TArray<AProjectile*> AvailableProjectiles;
    
    UPROPERTY()
    TArray<AProjectile*> ActiveProjectiles;
};

void AProjectilePool::InitializePool(TSubclassOf<AProjectile> ProjectileClass, int32 PoolSize)
{
    for (int32 i = 0; i < PoolSize; i++)
    {
        AProjectile* Proj = GetWorld()->SpawnActor<AProjectile>(ProjectileClass);
        Proj->SetActorHiddenInGame(true);
        Proj->SetActorEnableCollision(false);
        Proj->SetActorTickEnabled(false);
        AvailableProjectiles.Add(Proj);
    }
}

AProjectile* AProjectilePool::GetProjectile()
{
    if (AvailableProjectiles.Num() == 0)
    {
        UE_LOG(LogTemp, Warning, TEXT("Pool is empty! Consider increasing pool size."));
        return nullptr; // Ili dinamicki kreiraj jos jedan
    }
    
    AProjectile* Proj = AvailableProjectiles.Pop();
    Proj->SetActorHiddenInGame(false);
    Proj->SetActorEnableCollision(true);
    Proj->SetActorTickEnabled(true);
    Proj->ResetProjectile(); // Resetujte stanje na default
    ActiveProjectiles.Add(Proj);
    return Proj;
}

void AProjectilePool::ReturnProjectile(AProjectile* Projectile)
{
    Projectile->SetActorHiddenInGame(true);
    Projectile->SetActorEnableCollision(false);
    Projectile->SetActorTickEnabled(false);
    ActiveProjectiles.Remove(Projectile);
    AvailableProjectiles.Add(Projectile);
}

Prednosti pooling-a:

Mane pooling-a:

Sta je vredno pooling-a:

Objekat Pooling? Razlog
Projektili Da Cesto spawn/destroy, kratkog zivota
Particle efekti Delimicno UE5 Niagara vec ima interni pooling
Audio instance Delimicno UE5 audio ima vlastiti pooling
NPC neprijatelji Zavisno Ako se cesto spawn-uju u talasima
Pickup-ovi Da Cesto spawn/destroy
Decali (krv, rupe od metaka) Da Mnogo njih, kratkog zivota
Glavni likovi Ne Retko se spawn-uju/destroy-uju
Level geometry Ne Staticki, nikada se ne destroy-uju

49.4.3 Garbage Collection -- Tihi Ubica Performansi

UE5 koristi Garbage Collector (GC) za automatsko upravljanje memorijom UObject-a. GC periodocno skenira sve UObject-e, identifikuje one koji niko vise ne referencira, i brise ih.

Problem? GC se izvrsava na game thread-u i moze izazvati značajne pauze:

Tipicne GC pauze:
    Mali projekat (malo UObject-a):     0.5 - 2 ms
    Srednji projekat:                    2 - 10 ms
    Veliki projekat (mnogo UObject-a):   10 - 50+ ms

Na 60 FPS (budzet 16.67 ms), GC pauza od 15 ms znaci da cete propustiti barem jedan frame. Igrac to vidi kao "stucanje" ili micro-freeze.

Kako kontrolisati GC:

  1. Smanjite broj UObject-a koji se kreiraju i unistavaju: Ovo je najefektivnija strategija. Manje smeća = manje posla za GC.

  2. Podesavanje GC parametara:

    gc.MaxObjectsNotConsideredByGC=1     // Minimalan broj objekata koji GC ne razmatra
    gc.TimeBetweenPurgingPendingKillObjects=60  // Interval izmedju GC ciklusa
    gc.NumRetriesBeforeForcingGC=5       // Broj pokusaja pre forsiranog GC-a
    
  3. Inkrementalni GC: UE5 podrzava inkrementalni GC koji deli posao na vise frejm-ova:

    gc.IncrementalBeginDestroyEnabled=1   // Ukljuci inkrementalno unistavanie
    gc.MultithreadedDestructionEnabled=1  // Koristi vise niti za destrukciju
    
  4. Forsirani GC u kontrolisanim trenucima:

    // Forsirajte GC kada znate da igrac nece primetiti (loading screen, pauza)
    GEngine->ForceGarbageCollection(true);
    
  5. Koristite non-UObject tipove kada je moguce: FVector, FTransform, TArray -- ovi tipovi nisu UObject-i i ne prolaze kroz GC. Koristite ih za podatke koji ne trebaju UObject infrastrukturu (repliciranje, editovanje u editoru, itd.).

49.4.4 Cluster-based GC

UE5 ima koncept GC Clustering-a gde se grupa povezanih objekata tretira kao jedan "cluster" za potrebe GC-a. Umesto da GC skenira svaki objekat pojedinacno, skenira cluster kao celinu -- ako je root cluster-a ziv, svi objekti u cluster-u su zivi.

Ovo je posebno korisno za asset-e: StaticMesh sa svojim materijalima i teksturama formira cluster. Umesto da GC proverava svaki od tih objekata, proverava samo StaticMesh root.

// U Project Settings ili DefaultEngine.ini
[/Script/Engine.GarbageCollectionSettings]
gc.CreateGCClusters=True
gc.MinGCClusterSize=5

49.5 Async Loading: Eliminisanje Hitching-a

49.5.1 Sinhrono vs Asinhrono ucitavanje

Jedna od najcescih gresakaka u UE5 projektima je sinhrono ucitavanje asset-a. Evo sta se desava:

// LOSE: Sinhrono ucitavanje
UStaticMesh* Mesh = LoadObject<UStaticMesh>(
    nullptr, 
    TEXT("/Game/Meshes/BigTree.BigTree")
);
// Game thread je BLOKIRAN dok se ceo mesh ne ucita sa diska
// Ako mesh ima 50 MB, ovo moze da traje 100+ ms
// Igrac vidi freeze

Sinhrono ucitavanje blokira game thread dok se ceo asset ne ucita u memoriju. Za male asset-e, ovo moze proci neprimeceno. Ali za velike mesh-eve, teksture, ili Blueprint klase, mozete dobiti pauze od 50-200+ ms -- katastrofalni freeze koji igrac odmah primeti.

49.5.2 Soft References -- Kljuc Asinhronog Pristupa

Razlika izmedju hard reference-a i soft reference-a:

Hard Reference (UStaticMesh* ili TSubclassOf<AActor>):

Primer lanca ucitavanja sa hard reference-om:
    MojBP_Neprijatelj (1 MB)
        -> Mesh_Neprijatelj (15 MB)
            -> Material_Koža (2 MB)
                -> Texture_Diffuse (8 MB)
                -> Texture_Normal (8 MB)
        -> AnimBP (3 MB)
            -> Montage_Napad (500 KB)
                -> Sound_Napad (2 MB)
        -> BP_Projektil (500 KB)
            -> Mesh_Projektil (1 MB)
            -> Particle_Eksplozija (5 MB)
                -> ...
    
    Ukupno za jednog neprijatelja: ~46 MB
    Sve se ucitava ODJEDNOM, SINHRONO

Soft Reference (TSoftObjectPtr<UStaticMesh> ili TSoftClassPtr<AActor>):

// Deklaracija soft reference-a
UPROPERTY(EditDefaultsOnly, Category = "Assets")
TSoftObjectPtr<UStaticMesh> TreeMeshSoft;

// Asinhrono ucitavanje
void AMyActor::LoadTreeMesh()
{
    if (TreeMeshSoft.IsNull()) return;
    
    // Proveri da li je vec ucitano
    if (TreeMeshSoft.IsValid())
    {
        // Vec je u memoriji, koristi odmah
        MeshComp->SetStaticMesh(TreeMeshSoft.Get());
        return;
    }
    
    // Ucitaj asinhrono
    FStreamableManager& StreamableManager = 
        UAssetManager::GetStreamableManager();
    
    StreamableManager.RequestAsyncLoad(
        TreeMeshSoft.ToSoftObjectPath(),
        FStreamableDelegate::CreateUObject(
            this, 
            &AMyActor::OnTreeMeshLoaded
        )
    );
}

void AMyActor::OnTreeMeshLoaded()
{
    if (TreeMeshSoft.IsValid())
    {
        MeshComp->SetStaticMesh(TreeMeshSoft.Get());
    }
}

49.5.3 StreamableManager i Asset Manager

StreamableManager je UE5 sistem za asinhrono ucitavanje asset-a. Pruza vise mogucnosti od prostog RequestAsyncLoad:

// Ucitaj grupu asset-a odjednom
TArray<FSoftObjectPath> AssetsToLoad;
AssetsToLoad.Add(MeshSoft.ToSoftObjectPath());
AssetsToLoad.Add(MaterialSoft.ToSoftObjectPath());
AssetsToLoad.Add(SoundSoft.ToSoftObjectPath());

StreamableHandle = StreamableManager.RequestAsyncLoad(
    AssetsToLoad,
    FStreamableDelegate::CreateLambda([this]()
    {
        // Svi asset-i su ucitani
        OnAllAssetsLoaded();
    }),
    FStreamableManager::DefaultAsyncLoadPriority,
    false  // bManageActiveHandle -- da li StreamableManager upravlja handle-om
);

Asset Manager je visi nivo apstrakcije nad StreamableManager-om. Omogucava vam da definisete "primarne asset tipove" i upravljate njihovim ucitavanjem:

// U AssetManager subclass-u
void UMyAssetManager::StartInitialLoading()
{
    Super::StartInitialLoading();
    
    // Registruj primarne asset tipove
    // Ovo odredjuje koje asset-e engine "zna" i moze da ucita na zahtev
}

// Ucitavanje primarnog asset-a
FPrimaryAssetId WeaponAssetId = FPrimaryAssetId("Weapon", "Rifle_AK47");
UAssetManager::GetIfValid()->LoadPrimaryAsset(
    WeaponAssetId,
    TArray<FName>(),  // Bundle-ovi koji se ucitavaju
    FStreamableDelegate::CreateUObject(this, &AMyActor::OnWeaponLoaded)
);

49.5.4 Preloading -- Ucitajte Pre Nego Sto Zatreba

Najbolji nacin da izbegnete hitching je da ucitate asset pre nego sto vam zatreba:

// Na pocetku nivoa, preload-ujte asset-e koji ce vam trebati
void AMyGameMode::BeginPlay()
{
    Super::BeginPlay();
    
    // Preload neprijatelje koji ce se pojaviti u ovom nivou
    for (const TSoftClassPtr<AEnemy>& EnemyClass : EnemiesInThisLevel)
    {
        StreamableManager.RequestAsyncLoad(
            EnemyClass.ToSoftObjectPath(),
            FStreamableDelegate() // Nema callback -- samo ucitaj u pozadini
        );
    }
}

Strategije preloading-a:

  1. Loading screen preload: Ucitajte sve asset-e dok je loading screen aktivan
  2. Proximity preload: Kada se igrac priblizi nekom delu nivoa, pocnite asinhrono ucitavanje asset-a za taj deo
  3. Anticipatory preload: Na osnovu gameplay-a, predvidite sta ce igracu trebati (npr. ucitajte boss mesh pre nego sto igrac udje u boss sobu)
  4. Bundle preload: Grupisite povezane asset-e u "bundle-ove" i ucitajte ih zajedno

49.5.5 Merenje hitching-a od ucitavanja

Koristite sledece alate da detektujete hitching od sinhronog ucitavanja:

stat asyncload           // Statistika asinhronog ucitavanja
stat streaming          // Streaming statistika
stat levels             // Level streaming statistika

U Unreal Insights-u, potrazite:


49.6 Level Streaming Optimization

49.6.1 Zasto je Level Streaming vazan?

Ni jedna igra ne moze da drzi ceo svet u memoriji odjednom. Otvoreni svetovi sa desetinama kvadratnih kilometara terena, hiljadama zgrada, i milionima objekata bi zahtevali stotine gigabajta RAM-a. Resenje je level streaming -- ucitavanje i oslobadjanje delova sveta dinamicki, na osnovu pozicije igraca.

UE5 nudi dva glavna sistema za ovo:

  1. World Partition (moderni pristup, pokriven u poglavlju 32) -- automatski deli svet na celije i strimuje ih
  2. Level Streaming (klasicni pristup) -- rucno kreirate sub-level-e i kontrolisete njihovo ucitavanje

49.6.2 Streaming Volumes

Level Streaming Volumes su 3D volumeni u svetu koji kontrolisu ucitavanje i oslobadjanje sub-level-a:

Kada igrac UDJE u volume -> Ucitaj sub-level
Kada igrac IZADJE iz volumena -> Oslobodi sub-level

Optimizacijske strategije za volume-e:

  1. Preload volume-i: Napravite vece volume-e koji se prostiru izvan vidljivosti igraca. Kada igrac udje u preload volume, pocnite ucitavanje. Kada udje u main volume, level je vec ucitan.
[     Preload Volume (veci)    ]
[                              ]
[   [  Main Volume (manji) ]   ]
[   [                      ]   ]
[   [    Level Content     ]   ]
[   [                      ]   ]
[   [______________________]   ]
[                              ]
[______________________________]
  1. Hysteresis: Dodajte razmak izmedju ucitavanja i oslobadjanja da izbegnete "thrashing" (konstantno ucitavanje i oslobadjanje kada je igrac na granici volumena).

  2. Prioritizacija: Ne ucitavajte sve level-e sa istim prioritetom. Level koji je ispred igraca treba da se ucita pre level-a koji je iza njega.

49.6.3 Programsko kontrolisanje streaming-a

Ponekad volume-i nisu dovoljni. Mozete kontrolisati streaming programski:

// Ucitaj sub-level asinhrono
UGameplayStatics::LoadStreamLevel(
    this,
    FName("SubLevel_CityBlock_3"),
    true,   // bMakeVisibleAfterLoad
    false,  // bShouldBlockOnLoad -- NIKADA ne stavljajte true osim za loading screen
    LatentInfo
);

// Oslobodi sub-level
UGameplayStatics::UnloadStreamLevel(
    this,
    FName("SubLevel_CityBlock_3"),
    LatentInfo,
    false  // bShouldBlockOnUnload
);

49.6.4 Loading Screens kao optimizacijski alat

Loading screen-ovi nisu samo kozmeticki -- oni su legitimno optimizacijski alat:

  1. Sinhrono ucitavanje tokom loading screen-a: Mozete koristiti sinhrono ucitavanje bez brige za hitching jer igrac ne vidi gameplay
  2. GC cleanup: Pokrenite potpuni GC ciklus tokom loading screen-a
  3. Precomputation: Izracunajte sve sto vam treba (NavMesh, AI patrolne rute, itd.)
void AMyGameMode::TransitionToLevel(FName LevelName)
{
    // Pokazi loading screen
    ShowLoadingScreen();
    
    // Sada mozemo raditi skupe stvari bez brige
    
    // 1. Ocisti prethodni level
    UnloadCurrentLevel();
    
    // 2. Forsiraj GC (bezbedno jer je loading screen aktivan)
    GEngine->ForceGarbageCollection(true);
    
    // 3. Ucitaj novi level (moze i sinhrono jer igrac ceka)
    LoadStreamLevel(LevelName, /*bShouldBlockOnLoad=*/true);
    
    // 4. Preload-uj asset-e za taj level
    PreloadAssetsForLevel(LevelName);
    
    // 5. Sakrij loading screen
    HideLoadingScreen();
}

49.6.5 Async Loading Priority

UE5 ima sistem prioriteta za asinhrono ucitavanje. Mozete kontrolisati koji asset-i imaju prednost:

// Visi prioritet = ucitava se ranije
static const int32 HighPriority = 100;
static const int32 NormalPriority = 50;
static const int32 LowPriority = 10;

StreamableManager.RequestAsyncLoad(
    CriticalAsset.ToSoftObjectPath(),
    Callback,
    HighPriority  // Ovaj asset ima prednost nad drugima
);

Prakticna pravila za prioritizaciju:

Tip asset-a Prioritet Razlog
Level geometry (mesh-evi, kolizija) Visok Igrac ne sme da padne kroz pod
Teksture Srednji Streaming vec upravlja mipmap-ovima
Zvukovi Nizak Mogu da kasne par frejmova bez problema
Animacije Srednji NPC-ovi mogu koristiti placeholder animaciju
Blueprint klase Visok (pre spawn-a) Moraju biti ucitane pre spawn-ovanja
Particle efekti Nizak Mogu da se ucitaju "lazy"

49.6.6 World Partition optimizacione napomene

Ako koristite World Partition (sto bi trebalo za otvorene svetove u UE5), evo kljucnih optimizacionih tacaka:


49.7 Multithreading Awareness: Iskoristite Sve Jezgre

49.7.1 UE5 Threading Model

Moderni CPU-ji imaju 8, 12, 16 ili vise jezgara. Ali vecina koda u UE5 (Blueprint Tick, gameplay logika) se izvrsava na jednoj niti -- game thread-u. Ovo znaci da ako imate 16-core procesor, 15 jezgara potencijalno sede besposlen dok game thread gusi jedno jezgro.

UE5 vec koristi vise niti za neke sisteme:

Game Thread          -- Logika igre, Blueprint, ticking
Render Thread        -- Priprema rendering komandi
RHI Thread           -- Graficki API pozivi
Physics Thread(s)    -- Chaos fizicka simulacija
Audio Thread         -- Audio mixing i processing
Animation Thread(s)  -- Bone evaluation (paralelno za vise skeletal mesheva)
Async Loading Thread -- Ucitavanje asset-a sa diska
Task Threads         -- Pool niti za razne zadatke (Task Graph)

Ali vasa gameplay logika (Tick, Blueprint) je na game thread-u. Da biste iskoristili ostala jezgra, morate eksplicitno koristiti multithreading.

49.7.2 Task Graph System

UE5 Task Graph je sistem za rasporedjivanje zadataka (tasks) na worker niti. Umesto da sami kreirate i upravljate nitima (sto je opasno i komplikovano), definisete "zadatke" i Task Graph ih rasporeduje na dostupne worker niti.

// Primer: Procesiranje velikog niza u pozadinskom thread-u
class FMyProcessingTask : public FNonAbandonableTask
{
    friend class FAutoDeleteAsyncTask<FMyProcessingTask>;
    
    TArray<FVector> DataToProcess;
    TArray<FVector> Results;
    
    FMyProcessingTask(const TArray<FVector>& InData)
        : DataToProcess(InData)
    {
    }
    
    void DoWork()
    {
        // Ovo se izvrsava na worker thread-u, NE na game thread-u
        Results.Reserve(DataToProcess.Num());
        for (const FVector& V : DataToProcess)
        {
            // Neki skupi proracun
            Results.Add(V.GetSafeNormal() * FMath::Sin(V.Size()));
        }
    }
    
    FORCEINLINE TStatId GetStatId() const
    {
        RETURN_QUICK_DECLARE_CYCLE_STAT(FMyProcessingTask, STATGROUP_ThreadPoolAsyncTasks);
    }
};

// Pokretanje task-a
void AMyActor::StartHeavyComputation()
{
    (new FAutoDeleteAsyncTask<FMyProcessingTask>(MyLargeDataArray))->StartBackgroundTask();
}

49.7.3 ParallelFor -- Paralelizacija Petlji

ParallelFor je najjednostavniji nacin da paralelizujete "for" petlju:

// SEKVENCIJALNO (jednonit):
for (int32 i = 0; i < BigArray.Num(); i++)
{
    BigArray[i] = ExpensiveComputation(BigArray[i]);
}

// PARALELNO (multi-nit):
ParallelFor(BigArray.Num(), [&](int32 Index)
{
    BigArray[i] = ExpensiveComputation(BigArray[Index]);
});

ParallelFor automatski deli posao na chunck-ove i rasporeduje ih na worker niti. Ako imate 8 jezgara i 1000 elemenata, svako jezgro obradjuje ~125 elemenata.

Kada koristiti ParallelFor:

Situacija ParallelFor? Razlog
10 elemenata, svaki je jeftin Ne Overhead thread-ova je veci od ustede
1000 elemenata, svaki je jeftin Mozda Zavisi od overhead-a; testirajte
100 elemenata, svaki je skup (1+ ms) Da Znacajna usteda
Elementi zavise jedni od drugih Ne Race conditions!
Elementi pristupaju deljenim podacima za pisanje Ne Race conditions!
Elementi samo citaju deljene podatke Da Citanje je thread-safe

Kriticno upozorenje: ParallelFor radi na worker nitima, ali NE na game thread-u. To znaci:

Koristite ParallelFor za "ciste" proracune -- matematiku, fiziku, path finding proracune, AI evaluaciju -- gde radite sa kopijama podataka, a rezultate aplicirate na game thread-u.

49.7.4 Async Tasks

Za dugotrajne operacije koje ne moraju da se zavrse u istom frame-u, koristite AsyncTask:

// Pokretanje async task-a koji se izvrsava na background thread-u
AsyncTask(ENamedThreads::AnyBackgroundThreadNormalTask, [this]()
{
    // Skup proracun na background thread-u
    TArray<FPathResult> Paths = ComputePatrolPaths(AllWaypoints);
    
    // Kada zavrsimo, vracamo rezultat na game thread
    AsyncTask(ENamedThreads::GameThread, [this, Paths = MoveTemp(Paths)]()
    {
        // Ovo se izvrsava na game thread-u -- bezbedno je pristupati UE5 API-jima
        ApplyPatrolPaths(Paths);
    });
});

Pattern za dvosmerni async rad:

  1. Pripremite podatke na game thread-u
  2. Pokrenite async task na background thread-u
  3. U async task-u, radite skup proracun
  4. Kada zavrsiste, koristite AsyncTask(GameThread, ...) da biste primenili rezultate na game thread-u

49.7.5 Kada NE koristiti multithreading

Multithreading nije uvek odgovor:

  1. Overhead: Kreiranje i sinhronizacija niti ima overhead. Ako je posao manji od ~0.1 ms, overhead moze biti veci od ustede.
  2. Kompleksnost: Multithreaded kod je tezi za debugging, testiranje i odrzavanje. Race conditions su jedni od najtezih bagova za pronalazenje.
  3. UE5 ogranicenja: Vecina UE5 gameplay API-ja nije thread-safe. Ne mozete bezbedno pristupati World-u, spawn-ovati aktore, ili modifikovati UObject-e sa background niti.
  4. Latencija: Async task daje rezultat u buducem frame-u. Ako vam rezultat treba odmah u ovom frame-u, async ne pomaze.

Pravilo palca: Koristite multithreading za "batch" proracune koji traju vise od 1 ms i ciji rezultati ne moraju da budu dostupni u istom frame-u.


49.8 Render Thread Optimization: Smanjite CPU Overhead Renderinga

49.8.1 CPU strana renderinga -- Podsecanje

Poglavlje 42 (Draw Calls i Batching) detaljno pokriva ovu temu. Ovde cemo se fokusirati na CPU-specificne aspekte render thread-a koje nismo pokrili tamo.

Render thread je odgovoran za pripremu svega sto GPU treba. Njegov CPU overhead zavisi od:

  1. Broja objekata u sceni -- svaki objekat mora da prodje kroz visibility test
  2. Broja vidljivih objekata -- svaki vidljivi objekat generise draw call(ove)
  3. Broja svetala -- svako svetlo utice na pripremu shadow pass-eva i light pass-eva
  4. Kompleksnosti scene hijerarhije -- dublja hijerarhija = vise scene traversal-a
  5. Broja materijala -- vise unikatnih materijala = vise state change-ova

49.8.2 Visibility i Culling -- CPU perspektiva

Pre nego sto GPU nacrta ista, CPU mora da odluci sta da nacrta. Ovaj proces -- visibility determination -- moze biti veoma skup za velike scene:

Frustum Culling: CPU proverava bounding box svakog objekta u sceni protiv frustum-a kamere. Objekti izvan frustum-a se preskazu. Ovo je O(N) operacija gde je N broj objekata u sceni.

Za scenu sa 50,000 objekata, frustum culling moze da traje 1-3 ms. Zvuci malo, ali zapamtite da je ovo samo prvi korak.

Occlusion Culling: Posle frustum culling-a, CPU proverava da li su preostali objekti zaklanjeni drugim objektima. UE5 koristi hardware occlusion queries (salje upite GPU-u iz prethodnog frejma) i software occlusion (rasterizira occlusion mesh-eve na CPU-u).

Optimizacije za visibility/culling:

1. Smanjite ukupan broj objekata u sceni
   - Koristite Instanced Static Mesh za ponavljajuce objekte
   - Merge-ujte male objekte u vece mesh-eve
   - Koristite HLOD za zamenu grupa udaljenih objekata

2. Poboljsajte bounding box preciznost
   - Lose fitovani bounding box-ovi uzrokuju lazne pozitive u frustum culling-u
   - Custom occlusion geometry moze pomoci za kompleksne objekte

3. Koristite Precomputed Visibility Volumes
   - Za zatvorene prostore (enterijeri, tuneli) gde staticka vidljivost moze da se pre-izracuna
   - Drasticno smanjuje CPU posao za visibility

4. Podesavajte cull distance
   - MinDrawDistance i MaxDrawDistance na mesh-evima
   - Cull Distance Volumes za automatsko skrivanje malih objekata na daljini
// Podesavanje max draw distance na mesh komponenti
MyMeshComponent->SetCullDistance(10000.0f); // Ne crtaj dalje od 100m

// Ili na nivou aktora
MyActor->SetActorHiddenInGame(bShouldHide);

49.8.3 Batching -- Pomozite Render Thread-u da Vam Pomogne

Render thread pokusava da "batch-uje" (grupise) draw call-ove sa slicnim stanjem. Sto su vam objekti slicniji (isti mesh, isti materijal), render thread efikasnije grupe:

Sta pomaze batching-u:

Sta ubija batching:

49.8.4 Scene Complexity i Render Thread

Render thread mora da prodje kroz celu scenu svaki frejm. Sto je scena kompleksnija (vise objekata, dublja hijerarhija, vise overlapping svetala), to render thread duze radi.

Prakticne mere za smanjenje render thread pritiska:

  1. Smanjite broj primitiva u sceni: Koristite stat scenerendering da vidite koliko primitiva imate:

    stat scenerendering
    

    Potrazite Mesh Draw Calls i Visible Static Mesh Elements. Cilj je da drzite ove brojeve sto nizim.

  2. Smanjite broj svetala: Svako non-Nanite svetlo dodaje overhead-a render thread-u. Koristite Lumen gde mozete. Za staticna svetla, koristite baked lighting. Ogranicite point/spot light-ove sa Attenuation Radius.

  3. Izbjegavajte dinamicke promene scene: Pomeranje, rotiranje, ili skaliranje staticnih objekata u runtime-u prisiljava render thread da azurira scene proxy-je. Ako objekat ne treba da se menja, oznacite ga kao statican (Mobility: Static).

  4. Koristite Nanite: Nanite prebacuje ogromnu kolicinu visibility/culling posla sa CPU-a na GPU. Za Nanite mesh-eve, render thread radi dramaticno manje posla jer ne mora da priprema individualne draw call-ove za svaki mesh.


49.9 Cesti CPU Bottleneck-ovi u UE5

49.9.1 Blueprint Overhead

Blueprint-i su fantastican alat za rapid prototyping i gameplay programiranje. Ali imaju izmerljiv CPU overhead u poredjenju sa ekvivalentnim C++ kodom.

Zasto su Blueprint-i sporiji od C++-a?

Blueprint kod se izvrsava kroz Blueprint Virtual Machine (VM) -- interpreter koji cita Blueprint bytecode i izvrsava ga korak po korak. Svaka Blueprint noda prolazi kroz:

  1. Interpretation overhead -- VM mora da dekodira instrukciju
  2. Type checking -- proverava tipove na runtime-u
  3. Indirection -- pristupa podacima kroz pointere na UProperty, ne direktno
  4. Nema optimizacija kompajlera -- C++ kompajler agresivno optimizuje (inlining, vectorization, loop unrolling), Blueprint VM to ne radi

Koliki je overhead? Zavisi od tipa operacije:

Relativni overhead Blueprint vs C++ (priblizno):

Matematicke operacije (Add, Multiply):     4-8x sporije
Property pristup (Get/Set varijable):       3-5x sporije
Function poziv:                             10-20x sporije
Array operacije (Add, Find, ForEach):       5-15x sporije
String operacije:                           2-5x sporije
UObject Cast:                               2-3x sporije

NAPOMENA: Ovi brojevi su PRIBLIZNI i variraju od verzije do verzije UE5.
Pojedinacna noda je i dalje VEOMA brza u apsolutnim brojevima.

Kada Blueprint overhead postaje problem:

Problem nastaje kada imate:

Prakticna resenja:

  1. Prebacite "hot path" na C++: Ne morate ceo projekat da pisete u C++. Identifikujte funkcije koje se pozivaju najcesce i koje su najskuplje, i samo njih prebacite na C++.

  2. Nativize Blueprint-e: UE5 moze da konvertuje Blueprint u C++ kod tokom build-a (Blueprint Nativization). Ovo eliminise VM overhead ali dodaje kompleksnost build sistemu.

  3. Izbegavajte skupe Blueprint cvorove u Tick-u:

    LOSE (svaki frejm):
    Event Tick -> Get All Actors of Class -> ForEach Loop -> Line Trace -> Branch
    
    BOLJE:
    Event Tick -> (samo proveri cached rezultat)
    Timer (svakih 0.5s) -> Get All Actors of Class -> cache rezultat
    
  4. Koristite C++ za matematiku i petlje: Blueprint je sjajan za logiku toka programa (if/else, state machines), ali za matematicke proracune i iteraciju nad velikim skupovima podataka, C++ je dramaticno brzi.

  5. Profilisanje Blueprint koda: Koristite stat game i Blueprint Profiler (Window > Developer Tools > Blueprint Debugger/Profiler) da identifikujete skupe Blueprint cvorove.

49.9.2 Excesivni Collision Queries

Collision query-ji (line trace, sweep, overlap) su jedni od najcescih "skrivenih" CPU bottleneck-ova:

// LOSE: Line trace svaki frejm od svakog NPC-a ka svakom drugom NPC-u
void ANPC::Tick(float DeltaTime)
{
    for (ANPC* OtherNPC : AllNPCs)
    {
        FHitResult Hit;
        GetWorld()->LineTraceSingleByChannel(
            Hit,
            GetActorLocation(),
            OtherNPC->GetActorLocation(),
            ECC_Visibility
        );
        // ...
    }
}
// 100 NPC-ova = 10,000 line trace-ova PO FREJMU

Zasto su collision query-ji skupi?

Svaki line trace ili overlap test mora da:

  1. Prodje kroz physics broadphase (AABB tree traversal)
  2. Izvrsi narrowphase test (geometrijski test protiv precizne geometrije)
  3. Sortira rezultate po udaljenosti (za multi-hit)
  4. Filtrira po channelu, profilu, ignore listi

Za jedan line trace, ovo traje ~0.005-0.05 ms. Ali pomnozite sa 1000 trace-ova po frejmu i imate 5-50 ms samo na collision query-jima.

Optimizacije:

  1. Smanjite frekvenciju: Ne radite trace svaki frejm. Koristite timer ili smanjeni tick interval.

  2. Koristite async trace: UE5 podrzava asinhrone trace-ove koji se izvrsavaju u sledecim frejmovima:

    FTraceHandle Handle = GetWorld()->AsyncLineTraceByChannel(
        EAsyncTraceType::Single,
        Start, End,
        ECC_Visibility
    );
    // Rezultat ce biti dostupan u narednim frejmovima
    // Proverite sa: GetWorld()->QueryTraceData(Handle, Result)
    
  3. Koristite jednostavniju geometriju za koliziju: Collision mesh ne mora da bude identican vizuelnom mesh-u. Koristite simple collision (kutije, sfere, kapsule) umesto complex collision.

  4. Smanjite trace duzinu: Kraci trace = manje objekata za testiranje.

  5. Koristite channel-e pravilno: Filtrirajte trace na specificne kanale da smanjite broj objekata za testiranje.

  6. Batch-ujte trace-ove: Umesto mnogo pojedinacnih trace-ova, koristite MultiLineTrace ili overlap sa vecim volumenom.

49.9.3 Navigation Mesh Updates

NavMesh je struktura podataka koju AI koristi za navigaciju. Azuriranje NavMesh-a je CPU-intenzivan proces:

Kada se NavMesh azurira?

Zasto je skupo? NavMesh regeneracija ukljucuje:

  1. Voxelizaciju scene geometrije
  2. Generisanje navigacionih poligona
  3. Kreiranje povezanosti (graph edges)
  4. Azuriranje tile-ova koji su pogojeni promenom
Tipicni NavMesh azuriranje cost:
    Jedan mali objekat se pomeri:     0.1 - 0.5 ms
    Veci objekat se pomeri:           0.5 - 2 ms
    Mnogo objekata se pomeri:         2 - 20+ ms
    Puna regeneracija velikog nivoa:  100+ ms (samo tokom build-a ili loading-a)

Optimizacije:

  1. Minimizujte dinamicke NavMesh modifikatore: Ako objekat ne utice na navigaciju, iskljucite mu bAffectsNavigation:

    MyMeshComponent->SetCanEverAffectNavigation(false);
    
  2. Koristite Navigation Invokers: Umesto da NavMesh pokriva ceo svet, generisite ga samo oko igraca:

    Project Settings > Navigation System > 
        Runtime Generation: Dynamic
        Supported Agents > Navigation Data Gathering Mode: Lazy
    
  3. Podesavajte tile size: Manji tile-ovi znace manje regeneracije po promeni, ali vise tile-ova ukupno.

  4. Koristite NavMesh Bounds Volume: Ogranicite gde se NavMesh uopste generise.

  5. Izbegavajte ceste promene: Ako imate dinamicke prepreke (npr. vrata), koristite NavLink-ove umesto regeneracije NavMesh-a.

49.9.4 Animation Evaluation

Skeletal animation evaluation moze biti jedan od najskupljih CPU procesa, posebno sa mnogo animiranih likova:

Sta kosta u animation evaluation?

  1. AnimGraph evaluacija -- prolazak kroz AnimBP cvorove
  2. Blend evaluacija -- blendovanje izmedju vise animacija
  3. IK (Inverse Kinematics) -- posebno Full Body IK
  4. Bone transform computation -- racunanje finalne pozicije svakog bone-a
  5. Physics asset update -- za ragdoll i cloth
  6. Curve evaluation -- animacione krive (morph targets, custom curves)
Tipicni animation cost po liku:
    Jednostavan AnimBP (idle/walk/run):    0.05 - 0.15 ms
    Kompleksan AnimBP sa IK:               0.2 - 0.5 ms
    Full Body IK + fizika:                 0.5 - 2 ms
    Ragdoll:                               0.3 - 1 ms

Za 50 NPC-ova sa kompleksnom animacijom, to moze biti 10-25 ms -- daleko vise od celog frame budzeta na 60 FPS.

Optimizacije:

  1. URO (Update Rate Optimization): UE5 moze da smanji frekvenciju evaluacije animacija na osnovu udaljenosti od kamere:

    // Na SkeletalMeshComponent
    SkeletalMeshComp->VisibilityBasedAnimTickOption = 
        EVisibilityBasedAnimTickOption::OnlyTickPoseWhenRendered;
    

    Opcije:

    • AlwaysTickPoseAndRefreshBones -- Uvek evaluira (default, najskuplje)
    • AlwaysTickPose -- Uvek evaluira pozu ali refresh-uje bone-ove samo kada je rendovan
    • OnlyTickMontagesWhenNotRendered -- Tikuje samo montaze kada nije vidljiv
    • OnlyTickPoseWhenRendered -- Evaluira animaciju samo kada je vidljiv (najjeftinije)
  2. Anim Budget Allocator: UE5 plugin koji automatski upravlja animacionim budzetom -- ogranicava koliko ukupnog CPU vremena animacije mogu da potrose:

    Plugins > Animation Budget Allocator
    

    Ovaj sistem automatski smanjuje kvalitet animacija (skipuje frame-ove evaluacije) za manje vazne likove da bi odrzao ukupan budzet.

  3. Smanjite broj bone-ova: Manje bone-ova = brza evaluacija. LOD skeletal mesh-evi mogu imati redukovane skeleton-e.

  4. Simplificirajte AnimBP za daleke likove: Koristite AnimBP LOD sistem -- razlicite AnimBP grafove za razlicite LOD nivoe:

    // Registrujte LOD za AnimInstance
    // LOD 0: Pun AnimBP (blizu kamere)
    // LOD 1: Samo locomotion blend space
    // LOD 2: Samo idle animacija, nema IK
    
  5. Koristite Distance Matching umesto Root Motion za daleke likove: Root Motion je skuplji jer zahteva preciznu sinhronizaciju sa animacijom.

49.9.5 AI Perception

AI Perception sistem u UE5 moze biti znacajan CPU trosak:

Sta radi AI Perception?

Zasto je skupo?

Sight sense je obicno najskuplji jer za svaki AI aktor mora da:

  1. Pronadje sve potencijalne targete u range-u
  2. Za svaki target, proveri da li je u FOV (field of view)
  3. Za svaki target u FOV-u, uradi line trace da proveri vidljivost (line of sight)

Za 50 AI aktora sa 10 potencijalnih targeta svaki, to je potencijalno 500 line trace-ova po update-u.

Optimizacije:

  1. Smanjite Sense Update Interval:

    // Podrazumevani interval je 0.5 sekunde -- mozda mozete povecati
    PerceptionComponent->SetSenseUpdateInterval(1.0f); // Azuriraj samo jednom u sekundi
    
  2. Ogranicite Max Age: Sense stimuli koji su stariji od Max Age se brisu, smanjujuci memoriju i CPU za procesiranje.

  3. Smanjite Sight Range: Ne treba svakom AI aktoru sight range od 5000 jedinica. Razmislite koliko daleko AI zaista treba da vidi za gameplay koji imate.

  4. Koristite Affiliation filtering: Konfiguriste detection po "afiliaciji" (friendly, neutral, hostile) da smanjite broj proveravanih targeta.

  5. Koristite Team-based filtere: Implementirajte IGenericTeamAgentInterface da biste automatski filtrirali nebitne targete.

  6. Alternativa -- Custom Query umesto Perception: Za jednostavne slucajeve, razmislite o sopstvenoj, laksoj implementaciji:

    // Umesto AI Perception sistema sa svim njegovim overhead-om,
    // jednostavna sferna provera + jedan line trace moze biti dovoljna
    void ASimpleAI::CheckForPlayer()
    {
        float DistSq = FVector::DistSquared(GetActorLocation(), PlayerLocation);
        if (DistSq < SightRangeSq)
        {
            FHitResult Hit;
            if (!GetWorld()->LineTraceSingleByChannel(Hit, GetActorLocation(), 
                PlayerLocation, ECC_Visibility))
            {
                // Vidimo igraca
                OnPlayerSeen();
            }
        }
    }
    // Pozivajte ovo sa timerom svakih 0.3-1.0 sekundi
    

49.10 Profiling CPU sa Unreal Insights i Stat Komandama

49.10.1 Podsecanje na Osnovne Alate

Poglavlje 40 detaljno pokriva sve profiling alate. Ovde cemo se fokusirati na one koji su najkorisniji specificno za CPU analizu.

stat unit -- Identifikacija bottleneck thread-a:

stat unit

Ovo je UVEK prvi korak. Gledajte Game vs Draw vs GPU vrednosti:

Game: 12 ms    <-- Ako je ovo najvece, problem je na game thread-u
Draw:  8 ms    <-- Ako je ovo najvece, problem je na render thread-u
GPU:   6 ms    <-- Ako je ovo najvece, problem je na GPU-u

49.10.2 Game Thread Profiling

Za dublju analizu game thread-a:

stat game:

stat game

Prikazuje vreme potroseno na razlicite sisteme unutar game thread-a:

stat anim:

stat anim

Detaljne statistike animacionog sistema:

stat ai:

stat ai

AI sistem statistike:

stat physics:

stat physics

Fizicki sistem:

stat collision:

stat collision

Collision query statistike -- posebno korisno za identifikaciju excesivnih trace-ova.

stat navmesh:

stat navmesh

Navigation mesh statistike:

stat dumpticks:

dumpticks

Stampa SVAKI aktor i komponentu koja tikuje, sa frekvencijom. Neophodno za identifikaciju nepotrebnog ticking-a.

49.10.3 Render Thread Profiling

Za analizu render thread bottleneck-a:

stat scenerendering:

stat scenerendering

Prikazuje:

stat initviews:

stat initviews

Specijalizovano za visibility/culling:

Ako je stat initviews vreme veliko, imate previse objekata u sceni za visibility sistem.

49.10.4 Unreal Insights -- Duboka CPU Analiza

Unreal Insights (pokriven u poglavlju 40) je najmoconiji alat za CPU analizu. Evo specificnog workflow-a za CPU debugging:

Korak 1: Pokrenite snimanje

// Pokrenite igru sa trace-om
UnrealEditor.exe MyProject.uproject -trace=cpu,frame,bookmark

Ili iz editora:

Tools > Run Unreal Insights > Start Trace

Korak 2: Reprodukujte problem

Igrajte igru i reprodukujte tacnu situaciju gde imate CPU problem (npr. udjite u scenu sa mnogo NPC-ova).

Korak 3: Analizirajte trace u Insights-u

Otvorite trace file u Unreal Insights. Gledajte:

  1. Timing view -- gornji deo ekrana pokazuje sve niti i koliko vremena svaki blok trosi
  2. Game thread -- potrazite najduze blokove:
    • TickActors -- koliko ukupno trosi ticking?
    • BlueprintVM -- koliko trosi Blueprint izvrsavanje?
    • Physics -- koliko trosi fizicka simulacija?
    • AnimGameThread -- koliko trose animacije?
    • GarbageCollection -- da li GC izaziva spike-ove?
  3. Render thread -- potrazite:
    • InitViews -- visibility/culling
    • BasePass -- draw call priprema
    • ShadowDepths -- shadow priprema

Korak 4: Drill Down

Kliknite na blok da vidite children -- koji pod-sistem, koji aktor, koja funkcija trosi najvise vremena. Insights vam omogucava da precizno identifikujete "koji aktor sa kojom funkcijom trosi 5 ms po frejmu" -- sto je nemoguce sa stat komandama.

49.10.5 Custom Stat Markeri

Za vas sopstveni kod, dodajte custom stat markere da biste mogli da ih vidite u profajleru:

// Deklarisite stat grupu (u .cpp fajlu)
DECLARE_STATS_GROUP(TEXT("MyGame"), STATGROUP_MyGame, STATCAT_Advanced);

// Deklarisite individualni stat
DECLARE_CYCLE_STAT(TEXT("AI Update"), STAT_AIUpdate, STATGROUP_MyGame);

// Koristite u kodu
void AMyAIController::UpdateAI()
{
    SCOPE_CYCLE_COUNTER(STAT_AIUpdate);
    
    // Vas kod ovde...
    // Vreme unutar ovog scope-a ce se pojaviti u profajleru
    // kao "AI Update" unutar grupe "MyGame"
}

Sada mozete videti vas custom stat sa:

stat MyGame

I u Unreal Insights-u ce se pojaviti kao marker na game thread-u.

Savet: Dodajte stat markere na SVAKI vazan sistem u vasoj igri. To je mali overhead u runtime-u (~0.001 ms po markeru) ali ogromna pomoc pri debugovanju.

49.10.6 Prakticni CPU Profiling Workflow

Evo kompletnog workflow-a za resavanje CPU bottleneck-a:

1. stat unit
   -> Koji thread je problem? Game ili Draw?

2a. Ako je GAME thread:
    -> stat game           (opsti pregled)
    -> stat physics        (da li je fizika kriva?)
    -> stat anim           (da li su animacije krive?)
    -> stat ai             (da li je AI kriv?)
    -> stat collision      (da li su collision queries krivi?)
    -> dumpticks           (da li tikuje previse aktora?)
    -> Unreal Insights     (precizna identifikacija funkcije)

2b. Ako je DRAW (render) thread:
    -> stat scenerendering (koliko draw call-ova, primitiva?)
    -> stat initviews      (koliko visibility/culling trosi?)
    -> stat RHI            (koliko RHI overhead?)
    -> Unreal Insights     (precizna identifikacija)
    -> Pogledajte poglavlje 42 za draw call optimizacije

3. Identifikujte TOP 3 najskuplje stavke

4. Primenite optimizacije (iz ovog poglavlja)

5. Merite ponovo

6. Ponovite dok ne postignete target

49.11 Kompletni Primer: Od Problema do Resenja

Hajde da prodjemo kroz realan primer optimizacije CPU performansi.

Scenario

Igrate otvoreni svet RPG. Imate selo sa 200 NPC-ova. Kada igrac udje u selo, FPS padne sa 60 na 28.

Korak 1: Identifikacija

stat unit:
    Frame: 35.7 ms
    Game:  31.2 ms    <-- PROBLEM
    Draw:   8.4 ms
    GPU:   14.1 ms

Game thread je jasno bottleneck.

Korak 2: Dijagnoza

stat game:
    Tick Time: 22.4 ms    <-- Ogromno!
    
stat anim:
    AnimEval: 8.2 ms      <-- Skupo
    
stat ai:
    Perception: 4.1 ms   <-- Znacajno
    BehaviorTree: 3.8 ms  <-- Znacajno
    
dumpticks:
    200 NPC aktora tikuje (svaki frejm)
    200 SkeletalMeshComponent tikuje
    200 AIPerceptionComponent tikuje
    200 CharacterMovementComponent tikuje

Korak 3: Plan optimizacije

Na osnovu profajliranja, identifikujemo tri glavna problema:

  1. Svih 200 NPC-ova tikuje svaki frejm
  2. Svih 200 animacija se evaluira svaki frejm
  3. AI Perception radi proveravanja za svih 200 NPC-ova

Korak 4: Implementacija

Optimizacija 1: Significance-based Ticking

// NPC-ovi dalji od 30m tikuju sa smanjenom frekvencijom
// NPC-ovi dalji od 100m ne tikuju uopste
void ANPC::UpdateSignificance(float NewSignificance)
{
    if (NewSignificance > 0.7f) // Blizu igraca
    {
        SetActorTickInterval(0.0f);
        SetActorTickEnabled(true);
    }
    else if (NewSignificance > 0.3f) // Srednja udaljenost
    {
        SetActorTickInterval(0.2f);
        SetActorTickEnabled(true);
    }
    else // Daleko
    {
        SetActorTickEnabled(false);
    }
}

Optimizacija 2: Animation LOD

// Daleki NPC-ovi koriste simplifikovane animacije
SkeletalMeshComp->VisibilityBasedAnimTickOption = 
    EVisibilityBasedAnimTickOption::OnlyTickPoseWhenRendered;

Optimizacija 3: AI Perception tuning

// Smanjite frekvenciju perception update-a za daleke NPC-ove
// Bliski: 0.5s, srednji: 1.0s, daleki: potpuno iskljucen
PerceptionComponent->SetSenseUpdateInterval(UpdateInterval);

Korak 5: Rezultati

stat unit (posle optimizacije):
    Frame: 14.8 ms
    Game:   9.6 ms     <-- Sa 31.2 na 9.6 ms!
    Draw:   8.2 ms
    GPU:   14.0 ms

Od 28 FPS na preko 60 FPS. Bez ikakvih vizuelnih promena za igraca -- daleki NPC-ovi i dalje izgledaju zivo jer tikuju sa smanjenom frekvencijom (dovoljno za njihovu locomotion animaciju), a bliski NPC-ovi rade punom brzinom.


49.12 Sveobuhvatna Checklista za CPU Optimizaciju

Pre nego sto zavrsimo ovo poglavlje, evo kompletne checkliste koju mozete koristiti kao referentni vodic:

Game Thread

Render Thread

Specifcni Sistemi

Opste


Rezime Poglavlja

Ovo poglavlje je pokrilo sve kljucne aspekte CPU optimizacije u Unreal Engine 5. Hajde da sumiramo najbitnije lekcije:

  1. CPU ima dva glavna thread-a: Game thread (logika igre) i Render thread (priprema renderinga). Rade paralelno, ali jedan moze da blokira drugi. stat unit vam odmah kaze koji je bottleneck.

  2. Tick budgeting je kriticno: Svaki aktor i komponenta koja tikuje trosi CPU vreme. Smanjite frekvenciju tikovanja, iskljucite tick za neaktivne aktore, koristite timer-e umesto tick-a, i implementirajte Significance Manager za automatsko upravljanje.

  3. Komponente treba pazljivo birati i upravljati njima: Deaktivirajte nekoristene komponente, birajte lakse alternative (Static vs Skeletal Mesh, HISM vs mnogo odvojenih mesh-eva), i kontrolisite component tick nezavisno od actor tick-a.

  4. Object pooling eliminise spawn/destroy overhead: Za objekte kratkog zivota (projektili, efekti, pickup-ovi), reciklirajte umesto da kreirate i unistavate. Ovo takodje smanjuje GC pritisak.

  5. Asinhrono ucitavanje je obavezno: Sinhrono ucitavanje blokira game thread i izaziva vidljivo stucanje. Koristite Soft Reference-e, StreamableManager i Asset Manager za asinhrono ucitavanje. Preload-ujte asset-e pre nego sto vam zatrebaju.

  6. Level streaming zahteva pazljivo planiranje: Koristite preload volume-e, podesavajte prioritete ucitavanja, i koristite loading screen-ove kao priliku za tezak posao (GC, preloading, precomputation).

  7. Multithreading moze pomoci ali ima ogranicenja: ParallelFor i AsyncTask mogu da prebace tezak posao na worker niti, ali vecina UE5 API-ja nije thread-safe. Koristite multithreading za "ciste" proracune i aplicirajte rezultate na game thread-u.

  8. Render thread se optimizuje smanjivanjem scene kompleksnosti sa strane CPU-a: Manje objekata, bolji batching, Nanite za staticnu geometriju, HLOD za daleke grupe, i pravilne cull distance.

  9. Pet najcescih CPU ubica: Blueprint overhead u tick-u, excesivni collision query-ji, NavMesh regeneracija, animaciona evaluacija na stotinama likova, i AI Perception na previise NPC-ova.

  10. Profajliranje je temelj svega: stat unit za identifikaciju thread-a, specificne stat komande za sistem, dumpticks za tick auditing, Unreal Insights za preciznu analizu, i custom stat markeri za vas kod.


Kljucni Pojmovi

Termin Objasnjenje
Game Thread Glavna nit engine-a koja izvrsava svu logiku igre: Tick, Blueprint, fiziku, AI, navigaciju
Render Thread Nit koja priprema rendering komande za GPU: visibility, culling, draw call priprema, sorting
RHI Thread Nit koja prevodi engine rendering komande u graficki API pozive (DX12, Vulkan)
Tick Funkcija koja se poziva na aktoru ili komponenti svaki frejm (ili sa podesenim intervalom)
Tick Interval Minimalno vreme izmedju dva poziva Tick funkcije; 0 = svaki frejm
Tick Group Kategorija koja odredjuje kada u frejmu se tick izvrsava (PrePhysics, DuringPhysics, PostPhysics, itd.)
Significance Manager UE5 sistem za upravljanje "znacajnoscu" objekata na osnovu udaljenosti i drugih faktora
Object Pooling Tehnika gde se objekti recikliraju umesto da se kreiraju i unistavaju
Garbage Collection (GC) Automatsko ciscenje memorije nekoriscenih UObject-a; moze izazvati pauze
GC Clustering Grupisanje povezanih UObject-a u klastere za efikasniji GC
Soft Reference Referenca na asset koja cuva samo putanju, ne ucitava asset dok se eksplicitno ne zatrazi
Hard Reference Referenca na asset koja forsira ucitavanje asset-a cim se ucita objekat koji ga referencira
StreamableManager UE5 sistem za asinhrono ucitavanje asset-a
Asset Manager Visi nivo apstrakcije nad StreamableManager-om za upravljanje primarnim asset tipovima
Level Streaming Dinamicko ucitavanje i oslobadjanje delova sveta (sub-level-a) na osnovu pozicije igraca
World Partition Moderni UE5 sistem koji automatski deli svet na celije i strimuje ih
Task Graph UE5 sistem za rasporedjivanje zadataka na worker niti
ParallelFor Funkcija za paralelizaciju for petlji na vise niti
AsyncTask Mehanizam za pokretanje zadataka na pozadinskim nitima
Blueprint VM Virtual Machine koja interpretira Blueprint bytecode; sporija od nativnog C++ koda
URO (Update Rate Optimization) Sistem za smanjenje frekvencije evaluacije animacija na osnovu vidljivosti i udaljenosti
Anim Budget Allocator Plugin koji automatski upravlja ukupnim CPU budzetom za animacije
AI Perception UE5 sistem koji omogucava AI aktorima da "osecaju" svet (vid, sluh, steta, itd.)
NavMesh Navigation Mesh -- struktura podataka za AI navigaciju u svetu
FlushRenderingCommands Funkcija koja blokira game thread dok render thread ne zavrsi sav pending posao; ubija paralelizam
Collision Query Line trace, sweep ili overlap test protiv fizicke geometrije scene
Precomputed Visibility System koji pre-racunava vidljivost objekata za zatvorene prostore, smanjujuci runtime CPU cost
Cull Distance Maksimalna udaljenost na kojoj se objekat crta; objekti dalje od ove udaljenosti se preskazu
HLOD Hierarchical Level of Detail -- zamenjuje grupe udaljenih objekata jednim simplifikovanim proxy mesh-om

Veze sa Drugim Poglavljima


Preporuceno Citanje i Resursi

Zvanicna dokumentacija

Epic Games prezentacije i resursi

Knjige i eksterni resursi

Video resursi