Nella mia breve (ma intensa) esperienza di sviluppo di videogames, mi è capitato di assistere ad un fenomeno interessante, che ho cercato di studiare in modo più o meno scientifico. Come ogni ricerca scientifica, il tutto parte da un’osservazione empirica, che mi ha portato ad approfondirne i meccanismi ed a rintracciarne le cause.
L’osservazione, peraltro facilmente verificabile da chiunque, è che la complessità e la fluidità grafica di un tipico gioco per Nintendo DS superano quella di gran parte dei giochi per smartphone Nokia. Nello specifico ci tengo a precisare che l’osservazione si riferisce al periodo in cui avevo iniziato a sviluppare su DS, quindi circa 3 anni fa. Ovviamente le cose sono cambiate, soprattutto dal punto di vista della “forza bruta” degli smartphone, ma il discorso ha un respiro più ampio come vi mostrerò.
Lo smartphone di riferimento per le osservazioni era un best seller della Nokia di fascia media, il vendutissimo 5800 XpressMusic. Un dispositivo senza infamia e senza lode, con una CPU della famiglia ARM11 da 434 MHz, 128 MB di memoria RAM, display da 640×360 pixel e una serie di sensori che si fanno apprezzare per lo sviluppo di giochi (accelerometro, touch screen, GPS).
Il dispositivo in questione non era dotato di accelerazione 3D in hardware, funzione che era presente sui modelli precedenti di fascia alta (N95), poi è stata eliminata (N96) e che è stata reintrodotta nuovamente dopo qualche anno (N8).
Per dovere di cronaca, le stesse osservazioni si possono fare anche con i dispositivi dotati di accelerazione 3D in hardware, ma sono meno eclatanti e quindi probabilmente non mi avrebbero spinto ad approfondirne i meccanismi.
In primis, come intuibile, la mancanza di accelerazione 3D obbliga a puntare in basso con la grafica, per ottenere un framerate dignitoso anche in software rendering.
Per appianare le divergenze ho studiato il comportamento nei giochi 2D only. Anche qui il confronto è impietoso. Grazie alla sua avanzata gestione della grafica 2D, il Nintendo DS mostra sempre scene fluide con effetti complessi (rotazione e scaling in realtime, parallax scrolling, alpha blending, ecc…), quando i giochi per il Nokia non riescono a dare schermate fluide a pieno framerate (i classici 60 FPS degli schermi LCD) nemmeno con una semplice pallina che rimbalza su uno sfondo perfettamente statico.
Datasheet e calcolatrice alla mano, il tempo necessario per effettuare il disegno dello schermo, sul dispositivo Nokia, è di 5 millisecondi, e tra un frame e l’altro ci sono “ben” 16,7 millisecondi di tempo per far tutto.
Il framerate più “roseo” che riesco ad ottenere si assesta sui 20 FPS, ciò significa che il tempo necessario al dispositivo per generare una schermata è compreso tra 33.3 e 50 ms, diciamo 40 ms in media.
Se vi state chiedendo come ho fatto i calcoli, considerate che in condizioni stazionarie (tempo di rendering costante per ogni frame), aggiornando lo schermo soltanto durante il periodo di Vertical Blank, posso ottenere soltanto un sottomultiplo intero di 60 FPS, quindi 30, 20, 15, 12 e così via. Inoltre considero una stima per eccesso di 10 cicli di clock per disegnare un pixel senza alpha blending (stima ricavata empiricamente tramite codice scritto ad hoc).
Quindi se ho 640×360 pixel, e impiego 10 cicli di clock per pixel, mi serviranno circa 2.3 milioni di cicli di clock per aggiornare lo schermo. Ad una frequenza di clock di 434MHz, bastano 5.3 millisecondi per completare l’operazione, a cui si aggiungono 35 ms circa di overhead per arrivare ai 40ms calcolati precedentemente.
Rifacendo i calcoli sul Nintendo DS, usando un algoritmo di software rendering che impiega circa 20 cicli di clock per pixel (il DS fa il doppio dei cicli a causa del bus a 16 bit della Ram), otteniamo: 256×192 pixel x 20 cicli = 983040 cioè quasi un milione di cicli di clock necessari, che con una CPU a 67MHz significano circa 15 ms. Sul DS ottengo, nonostante tutto, 60 FPS pieni, quindi a questi 15 ms si aggiunge pochissimo overhead, rientrando perfettamente nel limite dei 16.7 ms necessari per l’aggiornamento dello schermo. Nel caso peggiore abbiamo sul DS circa 1 ms di overhead, contro i 35 ms misurati sullo smartphone, usando in entrambi i casi un algoritmo di software rendering.
A questo punto la questione si sposta sul sistema operativo sottostante, Symbian OS.
Symbian OS è un sistema operativo multitasking, con caratteristiche Real Time. La parola Real Time non deve trarre in inganno: Real Time non è un sinonimo di “veloce” e nemmeno di “multimediale”. Un sistema operativo si definisce Real Time quando offre la possibilità di schedulare dei task con precise indicazioni di deadline. In altre parole, un sistema operativo si dice Real Time quando mi consente di chiedergli “mi serve che tu esegua questo task entro i prossimi 300 millisecondi” e mantiene le promesse in modo deterministico, cioè accetta la richiesta soltanto se sa di poter mantenere la promessa, altrimenti la rifiuta.
Il sistema, per garantire le deadline, deve effettuare dei calcoli ed accettare il task soltanto se è sicuro al 100% di rispettare tale deadline.
Il “come” è una materia estremamente complessa e mi scuso per la necessaria approssimazione che sto per fare. Nel caso specifico di Symbian OS, i task con deadline garantite sono quelli che gestiscono le comunicazioni radio. Cascasse il mondo, il task che si occupa di gestire la rete deve assolutamente essere eseguito entro i tempi prestabiliti. Ciò significa che esistono processi di serie A e processi di serie B (per così dire), e le applicazioni utente ricadono nella seconda categoria.
L’overhead misurato ricade in larga percentuale sui meccanismi necessari alla gestione di questi preziosissimi task, e in percentuale minore ad altri processi di serie B che concorrono all’assegnazione delle risorse con il nostro videogame, come ad esempio i processi che smistano gli eventi di sistema, il filesystem, la telefonia, il livello di carica della batteria, l’aggiornamento dei sensori, ecc…
Di contro, il Nintendo DS non ha alcun sistema operativo sottostante, quindi spetta al programmatore rispettare le proprie deadline (nel nostro caso, comporre la prossima schermata entro e non oltre i prossimi 16 millisecondi).
Nel prossimo articolo approfondiremo i meccanismi interni di un RTOS (Real Time Operating System) e cercheremo di capire se e come questi meccanismi possono essere d’aiuto nello sviluppo di videogiochi.