Perché molti giochi sviluppati usando l'Unreal Engine soffrono di problemi di ottimizzazione? Nel corso degli ultimi anni alcuni difetti stanno diventando sempre più evidenti e ricorrenti, in particolare su PC (ma non solo, in realtà), con titoli che richiedono grossi aggiornamenti post lancio per girare in modo decente. Naturalmente il problema non è diffuso solo tra i titoli che girano con l'UE (ne è un esempio The Last of Us Parte I su PC che utilizza un motore proprietario di Naughty Dog), ma quelli che sfruttano l'engine di Epic Games hanno comunque dei malfunzionamenti ricorrenti che si ripropongono simili di gioco in gioco, come l'ormai famigerato stuttering. Uno dei motivi è sicuramente la compilazione degli shader, di cui abbiamo parlato in un altro speciale, ma in realtà il motore di Epic Games ha un problema più profondo, che riguarda l'utilizzo dei thread della CPU, problema di lungo corso che la casa di Jazz Jackrabbit non ha mai affrontato o risolto, nonostante le molte richieste fatte dagli sviluppatori.
Per inciso, un thread della CPU è l'unità di base dell'elaborazione eseguita dal processore centrale. Quando un processo viene eseguito, i suoi thread vengono assegnati ai diversi core della CPU per l'esecuzione parallela. Ogni thread può eseguire un flusso indipendente di istruzioni e può avere accesso alle risorse condivise dal processo. Ora che abbiamo il quadro di ciò di cui stiamo parlando, torniamo a noi e cerchiamo di capire da cosa derivano i problemi di ottimizzazione di Unreal Engine 4 e 5 su PC e non solo, facendo però un'ulteriore premessa: non tutti i giochi escono o usciranno mal ottimizzati. Nella maggior parte dei casi gli sviluppatori riescono a lanciare dei prodotti in ottimo stato, a prescindere dai problemi che illustreremo qui di seguito, comunque presenti in fase di produzione.
Single thread, non multi-thread
Per capire dove risiede il problema, vediamo come funziona l'Unreal Engine. Avendo una visione molto generica della questione, per avere un quadro più preciso abbiamo parlato con chi ha una competenza della materia decisamente superiore alla nostra, ossia uno sviluppatore che utilizza Unreal Engine 5 giornalmente, è decisamente esperto di rendering, e lavora in uno studio di sviluppo tripla A. È voluto rimanere anonimo perché di fatto stiamo esprimendo un giudizio sull'engine e si parla di questioni interne molto specifiche.
Interrogato sulla questione ottimizzazione e sui problemi specifici del motore di Epic Games, per spiegarsi è partito illustrando il funzionamento dei motori precedenti: "Allora, prendiamo gli engine del periodo in cui uscì Half Life: Episode One. All'epoca si stavano diffondendo le prime CPU Dual Core. Valve, per sfruttarle, elaborò un sistema con due thread: il thread "update", su cui gira tutto il gioco (fisica, simulazione, personaggi e così via) e quello "render", che prende le cose generate dal thread "update" e le passa alla scheda grafica. I due thread sono un po' sfasati, in genere il thread render gira un po' di millisecondi dopo quello update, in modo da dare tempo all'update di generare delle cose da fare. Di solito il thread render è più leggero dell'update, quindi genera solo un lag limitato. In questo modo però si usano entrambi i thread e si allevia il costo computazionale."
Da notare che stiamo parlando di un motore di due decenni fa, quindi di problemi che sono noti da tempo. "Con l'arrivo delle CPU con tanti core, ci si è lentamente mossi verso architetture in cui si spezza il gioco in tanti piccoli task che possono essere presi da ogni thread. In questo modo si riescono a utilizzare tutti i core. Alcuni engine, tipo il Decima (il motore proprietario di Guerrilla Games, utilizzato per i capitoli di Horizon e dato in prestito a Kojima Production per Death Stranding), dividono la parte render dalla parte update facendo prima una e poi l'altra, ma fanno stare tutto in un frame, riducendo molto il lag." Lo sviluppatore ha voluto anche sottolineare che non parliamo di tecnologie nuovissime, ma già affermate da anni: "Questa cosa qua non è fantascienza: si faceva già ai tempi di Xbox 360 e PlayStation 3. Ad esempio mi pare che si comportassero in questo modo un Burnout, un Need for Speed e i giochi di corse di Codemasters." Per dire, si trattava di un sistema utile a usare le particolari unità di calcolo di PS3.
Ma ora veniamo all'Unreal Engine che, a detta dello sviluppatore, è rimasto in qualche modo ancorato al 2006 "con il sistema update thread-render thread. Loro chiamano il thread update "game thread". Per usare i core, che ora sono molti più di due, e spesso ogni core ha pure due thread, hanno elaborato questo sistema: il core "game" ha qualche task che può andare su tanti thread, tipo le animazioni, ma finisce lì. Il render thread può usare più core per generare i comandi della GPU. Quindi per la maggior parte del tempo, tutti i core che non sono quelli che fanno girare il game thread o il render thread, non fanno niente, a parte ogni tanto gestire un'animazione qua e là." Confermato che si tratta di un vero e proprio collo di bottiglia, specifico dell'Unreal Engine, e presente anche nell'Unreal Engine 5.2, l'ultimissima versione uscita da pochi giorni, lo sviluppatore ci ha spiegato i problemi che ne derivano creando videogiochi.
"È la nostra sofferenza. In pratica passiamo una marea di tempo a tagliare gli update degli oggetti, anche abbassando la qualità del gioco, quando basterebbe poter fare girare gli update sui vari thread. Ma non è possibile: tutto l'engine è pensato per fare gli update solo sul game thread. Se provi a fare diversamente, crasha come un pazzo. Ad esempio, se provi a settare una nuova posizione per un oggetto fuori dal game thread, va in crash, dando un messaggio d'errore specifico che dice "Puoi muovere le cose solo sul game thread". Dopo anni di richieste e suppliche, gli sviluppatori sono riusciti a ottenere le animazioni multi-thread, che sono state introdotte da circa quattro anni. Anche se pare siano state implementate essenzialmente per favorire Fortnite, altrimenti Epic Games non avrebbe mai ottenuto i 60 FPS su console.
"Quindi diciamo", ha concluso lo sviluppatore, "se hai 4 thread (in genere ogni core ha due thread) la CPU è più o meno ben sfruttata: game, renderer, un altro thread chiamato "RHI" se usi DX12 e un thread in più per le animazioni. Ogni core in più però è praticamente buttato. Ora pensa a questo: PS5 ha 16 thread hardware, quindi 12 di questi passano la maggior parte del tempo a non fare niente." E come mai Epic Games non sistema questa situazione? Secondo lo sviluppatore ci potrebbe essere un motivo specifico: "Personalmente credo che il motivo principale siano i Blueprint [uno strumento visuale di scripting integrato in Unreal Engine]: farli diventare multithreading sarebbe molto complicato perché bisognerebbe riscrivere un sacco di roba e probabilmente non offrire la retrocompatibilità."
Perché allora l’Unreal Engine è così diffuso?
Visto il problema che abbiamo illustrato, viene da chiedersi come mai l'Unreal Engine sia così adottato e diffuso in tutta l'industria dei videogiochi. In realtà ci preme sottolineare che, nel caso di specie, stiamo parlando di un singolo problema tecnico, che però non oscura i vantaggi offerti dalle altre caratteristiche del motore di Epic Games e dalla formula con cui viene distribuito. Intanto va detto che sviluppare da zero un engine altrettanto versatile, quindi adattabile ai vari contesti di sviluppo, richiederebbe investimenti che, semplicemente, molti editori e software house non possono permettersi. Anche perché vengono lanciati sempre meno giochi e non si riuscirebbe ad ammortizzarne facilmente i costi. Non siamo più in un'epoca in cui una singola persona, o un gruppo ristretto di sviluppatori, possono creare da zero l'engine di un videogioco con tutti gli strumenti che gli servono per funzionare, almeno non uno complesso al punto da consentire lo sviluppo di un tripla A.
Un altro punto a favore dell'Unreal Engine, che ricordiamo essere liberamente accessibile da chiunque, è la facilità con cui si trovano sviluppatori che sappiano usarlo. Per dire, quando si assume qualcuno per lavorare a un engine chiuso o poco diffuso, tipo il Decima di Guerrilla, ma anche il CryEngine di Crytek o il Frostbite di DICE, l'assunto ha bisogno di un tempo di addestramento più o meno lungo per imparare a conoscerlo, tempo che rallenta inevitabilmente i lavori. Difficilmente sul mercato si troverà qualcuno che sappia usare subito un engine interno, a parte qualche ex dipendente. L'Unreal Engine, invece, vista la sua diffusione, ha permesso il formarsi di una maggiore offerta di sviluppatori già formati che consentono di rinfoltire i team in maniera più rapida ed efficace, anche di fronte a momenti di crescita rapida o a cambi tecnologici. Questo, ad esempio, è uno dei fattori che ha convinto CD Projekt ad abbandonare il suo motore interno per passare all'Unreal Engine 5.
Un altro motivo per il quale l'Unreal Engine è adottatissimo è la quantità di strumenti extra che offre, un punto che non va sottovalutato. L'engine di Epic dispone di soluzioni praticamente per ogni aspetto della produzione, tra tool di compressione video, sistemi di gestione delle luci, tool di creazione dei modelli umani e quant'altro. Le soluzioni custom non mancano, ossia team che si riscrivono i loro tool all'interno dell'engine, spesso per esigenze specifiche, ma già il pacchetto base è decisamente completo, cosa che consente di abbattere i costi.
Ci preme dire, in chiusura dell'articolo, che naturalmente siamo coscienti di come ogni gioco possa avere dei problemi specifici, che vengono affrontati in modo diverso dai vari team. Quello che abbiamo illustrato va preso come un problema generale, che ciascuna software house affronta in diverso modo, anche in base alle sue competenze interne. Diciamo che ci piace pensarlo come un insight di una questione molto specifica, che può far capire quanto molte delle cose che diamo per scontate quando si parla di videogiochi, in realtà non lo siano.