Aah, un topic où il faut filer des détails techniques gore (a/k/a "appeau à Wild_Cat"). Ca faisait longtemps. ^^
/! Attention -- Wild_Pavé dans 2 lignes /!
La frame rate pour les nuls
Les images en mouvement n'existent pas. Dans jeu vidéo, comme dans un film ou un dessin animé, tout mouvement n'est qu'illusion. On fait ça en affichant une image à l'écran, puis en effaçant tout et en ré-affichant une image légèrement différente de la précédente. Plus l'intervalle entre les images est court, plus l'impression de fluidité est grande (logique). La frame rate d'un jeu (exprimée en images par seconde ou frames par seconde -- c'est la même chose) est le nombre de fois par seconde que le jeu est capable d'effectuer ce processus.
Pour vous donner un point de comparaison, les films "tournent" à 24 images par seconde, et les dessins animés traditionnels de bonne qualité (les Disney animés à la main) à 12 images par seconde (un anime cheapos genre
Dragon Ball Z peut descendre à 4 fps ou moins).
Je veux 1000 fps!
Il y a plusieurs limites à la frame rate d'un jeu:
1. Le simple fait d'effacer l'écran et d'afficher une image consomme des ressources processeur.
2. Un écran ne peut afficher qu'un nombre limité d'images par seconde (50 pour une TV PAL, 60 pour une NTSC, 85 à 100 pour un bon moniteur à tube cathodique): c'est sa fréquence de rafraîchissement, exprimée en Hertz (Hz). Afficher plus d'images par seconde que la fréquence de rafraîchissement de l'écran ne sert à rien. Si l'on affiche moins d'images que la fréquence de rafraîchissement de l'écran, ce n'est pas grave non plus: une même image reste à l'écran pendant plusieurs rafraîchissements.
3. Pour afficher une image, il faut savoir quoi mettre dessus.
La limite la plus contraignante est bien entendu la 3. En effet, entre l'affichage d'une image et de la suivante, il faut que le programme calcule ce qui sera présent dessus. Autrement dit, il faut que tout ce qui est nécessaire à son affichage ait été résolu, à savoir:
- Les commandes du joueur ("il a appuyé sur la gâchette droite, je fais quoi, patron?").
- La "physique" du jeu (par exemple, la vitesse de décélération de Mario lorsqu'il est en train de courir et que le joueur lâche la manette) et toutes les autres règles (dans un jeu doté d'un moteur physique comme
Max Payne 2, Halo ou Psi-Ops, ça peut devenir très compliqué très vite -- réactions en chaîne de grenades, particules, caisses projetées en l'air qui tournent de manière réaliste avant de rebondir sur un Warthog dont elles tuent l'artilleur...).
- Les scripts ("quand Gordon finit de pousser sa tondeuse à gazon dans l'accélérateur de particules, tout commmence à exploser").
- L'IA. Souvent dépendante de scripts, mais dans les jeux de stratégie ou de tactique, elle tente très souvent de prédire ce que le joueur va faire pour décider quoi faire elle-même (et c'est *très* gourmand, ça).
- Eventuellement, le streaming de données depuis le disque vers la RAM.
- Dans le cas d'un jeu multijoueurs, le trafic réseau.
Sans oublier que d'autres choses plus ou moins indépendantes de l'affichage ont lieu pendant ce temps-là (le son, par exemple). Une fois que tout cela est fait, on se heurte à la limite 1. Dans Dodonpachi, on sait qu'il faut afficher 30000 sprites parce que les 250 vaisseaux ennemis présents à l'écran ont décidé de vider leur réserve de munitions. Dans Burnout 3, on sait qu'il faut afficher 10 voitures à 5000 polygones l'unité, dont une est en train de se déformer et balance des étincelles (particules) dans tous les sens, calculer les effets d'éclairage correspondants, l'anti-alias et le filtre de motion blur qui est plus fort sur les bords de l'écran... Mine de rien, ça aussi ça consomme du temps CPU.
Garantir une frame rate constante
Là, vous devez être en train de vous dire, "Oui, mais, monsieur Cat, selon ce que je fais dans le jeu, cette consommation de temps machine ne doit pas être constante, si?" Et vous avez tout à fait raison. Prenons l'exemple de Halo 2:
- Scène 1. Le Master Chief est seul dans une petite salle, il ne fait rien et le décor ne bouge pas.
- Scène 2. Le Master Chief est dans New Mombassa, sur un pont suspendu à perte de vue, en train de massacrer des Covenants par dizaines depuis son tank. Un bus vient d'exploser sous l'effet d'une grenade à plasma, projetant en l'air un Ghost qui malgré tout continue de tirer. Un Banshee est en train de se crasher et le cadavre de son pilote d'atterrir sur les restes du bus. Dans le lointain, un Scarab sème la terreur avec son canon à plasma lourd. Une musique vient de démarrer, et Cortana explique qu'il faudrait que le Master Chief se bouge le popotin, parce qu'il y a des gens qui se font tuer, plus loin.
Devinez laquelle des deux scènes demande le plus de patate à calculer? La deuxième, c'est bien, vous suivez.
Du coup, si on veut avoir un jeu fluide en toutes circonstances, on prend la pire situation possible et on essaie de se débrouiller pour être capable d'afficher le nombre d'images par secondes désiré (30 dans le cas de Halo 2) pendant cette situation. Fort logiquement, cette cadence sera facile à atteindre pendant tout le reste du jeu. Et comme une frame rate constante donne l'impression d'un jeu beaucoup plus fluide qu'une qui varie tout le temps (un jeu à 30 fps constantes semble plus fluide qu'un qui oscille tout le temps entre 15 et 45), on la limite à ce nombre (le jeu n'en rendra jamais plus, quoi qu'il arrive). A ce stade-là, on peut affirmer "Halo 2 sur Xbox est un jeu qui tourne à 30 fps".
Se débrouiller avec une frame rate variable, ou "Saccade VS Ralentissement" (exclusif: pourquoi les jeux PAL sont plus lents que les jeux NTSC!)
Bien sûr, le coup de la frame rate constante, on ne peut le faire que sur console, quand le hardware est partout le même -- sur micro, il n'y a aucun moyen de garantir une frame rate minimale en toutes circonstances (à part en faisant tourner le jeu sur un supercalculateur).
Et bien c'est une bonne chose. En effet, si vous avez déjà joué à un jeu PC sur deux machines différentes dont l'une a plus de patate que l'autre, vous aurez remarqué que malgré la (grosse) différence de confort de jeu entre les deux (l'un des deux saccade à mort), le jeu tourne à la même vitesse (temporelle, pas hertzienne) partout. Et ça, c'est la classe. J'explique.
A cause de la manière dont fonctionnent les télévisions, une console ne peut envoyer une image à l'écran qu'à certains instants précis. Ce phénomène s'appelle la synchronisation verticale (ou VSync). La console "sait" quand elle peut envoyer une image, et du coup, chaque fois que le programme demande d'envoyer une image à l'écran, la console attend d'avoir pu le faire avant de poursuivre son exécution.
Or, sur console, les développeurs ont pendant longtemps fait un truc très con (et beaucoup, surtout au Japon, continuent à le faire): partir de la supposition erronée que le hardware est rigoureusement identique quoi qu'il arrive, et donc que leur console crache 60 images par seconde un point c'est tout. Donc, qu'une seconde égale 60 images.
Et d'exprimer toutes les vitesses dans leurs programmes en fonction des images et non du temps réel. Autrement dit, des choses comme "Le tir de l'arme principale avance d'un pixel par frame". Le jeu étant censé tourner à 60 images par seconde, ça paraît logique et acceptable...
Sauf que même sur console, le hardware n'est pas toujours identique. En Europe, les télévisions (PAL) sont en 50 Hz. Elles affichent 50 images par seconde. Vous vous rappelez du VSync? Et bien il s'applique toujours... Et là, c'est le drame.
"Le tir de l'arme principale avance d'un pixel par frame". Au Japon ou aux USA, cela signifie qu'il avance d'un pixel tous les 1/60e de seconde. En Europe, c'est tous les 1/50e de seconde. Résultat, en NTSC le tir avance de 60 pixels par seconde, et en Europe de 50 pixels par seconde: la version JP/US est donc 20% plus rapide!
Pendant ce temps-là, sur PC, les bons prorgammeurs se sont rendus compte que faire quelque chose comme ça est tout simplement ingérable si l'on veut obtenir un jeu qui tourne comme on le veut sur 30 processeurs, 500 modèles de moniteurs à 12 fréquences de rafraîchissement différentes. Alors, ils ont trouvé une solution simple et élégante: comme tout microprocesseur qui se respecte est capable de mesurer le temps réel, ils codent les vitesses dans leurs programmes en fonction du temps réel et non en fonction des images. Quand il faut mettre à jour une image, ils calculent le temps écoulé depuis la dernière mise à jour et en déduisent une variation en pixels. L'ordinateur affiche le nombre d'images par seconde qu'il peut (et ce nombre peut être variable sans que ça ait d'effets adverses).
Par exemple, sur PC, "Le tir de l'arme principale avance de 60 pixels par seconde":
- Le PC 1 tourne à 30 images par seconde. A la première image (t = 0), le tir de l'arme principale est à x = 0 pixels. A la deuxième image (soit t = 1/30 s), il est à x = 2 pixels (0 + 60*1/30).
- Le PC 2 tourne à 60 images par seconde. A la première image (t = 0), le tir est à x = 0 pixels. A la deuxième (t = 1/60 s), il est à x = 1 pixel (0 + 60*1/60). A la 3e image, il est à x = 2 pixels (1 + 60*1/60).
Le jeu est plus fluide sur le PC 2, mais il tourne exactement à la même vitesse sur les deux machines: c'est gagné!
L'inconvénient de cette méthode, c'est que lorsque le jeu dépasse les capacités de la machine, il saccade au lieu de ralentir comme c'est souvent le cas sur console (jouez à
Gradius III sur SNES, vous verrez ce que je veux dire) -- le ralenti est, je trouve, plus agréable à l'oeil.
Les avantages sont tout d'abord que même les très vieux jeux, s'ils ont été programmés correctement, continuent à tourner comme il faut sur les bécanes modernes (c'est, si je ne m'abuse, le cas de Popcorn et
Superfrog, tous deux testés sur GP), et ensuite que le jeu en réseau est rendu possible pour des joueurs dont les configs ne sont pas identiques. En effet, si les jeux utilisaient les frames au lieu d'un timer, il faudrait que tout le monde ralentisse chaque fois qu'une des machines a du mal à suivre (pour ne pas désynchroniser le réseau).
Bonus: Comment limiter la frame rate sur PC, indépendamment du hardware
Supposons que je programme un jeu et que comme
Doom 3 je veuille limiter sa frame rate à 60 fps (parce que je trouve que c'est suffisant, et qu'il faut garder un peu de temps CPU pour les tâches de fond). Je dois donc l'empêcher d'afficher plus de 60 images par seconde, ou plus exactement plus d'une image tous les 1/60e de seconde (parce que si le prog affiche 60 images en 1/10e de seconde et plus rien pendant 9/10e de seconde, je vais passer pour un con).
Et bien c'est très simple. Là aussi, il faut utiliser la capacité des processeurs à mesurer le temps réel. Si je sais quand la dernière image a été affichée et que je viens de finir tous les calculs nécessaires à l'affichage de la nouvelle, il me suffit d'attendre 1/60e de seconde moins le temps que j'ai mis à faire les calculs avant d'afficher la nouvelle image.
Voici un petit exemple en Python (je vous ai déjà dit à quel point j'aime ce langage?) qui affiche "foo" 2 fois par seconde:
Code:
|
import time
minDelay = 0.5 # On affiche au plus une image toutes les demi-secondes
timeOfLastFrame = time.time()
while True:
print "foo"
now = time.time()
delay = now - timeOfLastFrame # Combien de temps depuis la derniere image?
if delay < minDelay:
time.sleep(minDelay - delay) # Moins de 0.5 secondes: on attend...
timeOfLastFrame = now
|
|
Voilà! J'espère que ça vous a intéressé... A défaut, merci de bien vouloir réveiller ceux qui dorment au fond. Merci de votre attention!
-- Wild_Cat, over.
_________________
https://twitter.com/MaxNoelBass
https://www.youtube.com/c/TheTiberianSons