Sta-sta-stattering, ou d'où proviennent les micro-gel dans le jeu et comment les gérer

Imaginez: vous attendez ici une nouvelle partie de votre jeu préféré, et enfin elle sort. Surtout pour cette entreprise, vous avez mis à jour votre PC: installé les derniers CPU et GPU, augmenté la quantité de RAM et même remplacé le disque dur par un SSD. Votre jeu devrait maintenant fonctionner aussi bien que de la soie du tout premier écran de chargement à la toute fin.



Ici, vous téléchargez vous-même une pré-commande déjà payée. L'installation se termine, vous démarrez le jeu. Tout se passe bien: le jeu vole avec une fréquence d'images de 60 FPS. Ou du moins, c'est ce que vous indique le compteur d'images de votre superposition GPU. Mais quelque chose ne va pas. Vous déplacez votre souris d'avant en arrière et remarquez que le jeu se fige.



Comment est-ce possible? Quel autre gèle à 60 FPS?



Cela peut sembler ridicule jusqu'à ce que vous le rencontriez vous-même. Si vous avez rencontré de telles frises, vous avez probablement déjà réussi à les détester.



Ce ne sont pas des retards. Pas une faible fréquence d'images. C'est hallucinant. Avec un FPS élevé et une configuration ultra-rapide idéale.



De quoi s'agit-il, d'où vient-il et y a-t-il un moyen de s'en débarrasser? Découvrons-le maintenant.



image



Depuis l'introduction des premières machines d'arcade dans les années 70, les jeux vidéo tournent à 60 FPS. On suppose généralement que le jeu doit fonctionner à la même vitesse que l'affichage. Ce n'est qu'après la vulgarisation du jeu 3D que nous avons dû faire face et adopter une cadence plus faible. Dans les années 90, lorsque les "cartes 3D" (maintenant appelées "GPU") ont commencé à remplacer le rendu logiciel, les gens jouaient à 20 images par seconde, et 35 FPS était déjà considéré comme la fréquence d'une concurrence sérieuse sur les réseaux.



Maintenant, nous avons des voitures ultra-rapides qui peuvent bien sûr voler à 60 FPS. Cependant ... il semble qu'il y ait plus de performances insatisfaites que jamais. Comment est-ce possible?



Ce n'est pas que les jeux ne sont pas assez rapides. Et le fait qu'ils gèlent même avec des performances élevées.



Si vous parcourez les forums de jeux, vous verrez probablement quelque chose comme ça dans les gros titres:



image

les joueurs sur PC se plaignent souvent que les jeux souffrent de statter même s'il n'y a pas de problèmes de fréquence d'images.



On peut supposer qu'il s'agit de cas isolés, mais ces hypothèses sont dissipées par les statistiques de recherche Google:



image

au cours des 5 dernières années, le stattering est devenu (relativement) plus un problème que la performance.



(Notez que ce sont des valeurs relatives. Ce n'est pas que les gens recherchent plus souvent des informations sur le stattering que sur les fréquences d'images en général. Bien que le nombre de recherches pour la fréquence d'images reste le même, les recherches pour déclarer sont de plus en plus courantes. Surtout ces derniers temps. )





Décennie de recherche des raisons du statter



image

Le patient est définitivement vivant. Il s'inquiète souvent.



L'auteur a rencontré ce problème pour la première fois en 2003 alors qu'il travaillait sur Serious Sam 2. Les gens ont commencé à signaler des cas où les mouvements de l'écran et de la souris n'étaient pas fluides pendant les tests sur un niveau vide. Ceci était accompagné d'un modèle très spécifique dans le graphique de fréquence d'images, que l'équipe de développement a appelé "battement de cœur".



La première pensée était que quelque part dans le code un bogue s'était glissé, mais personne ne pouvait le trouver. Il semblait que le problème apparaissait et disparaissait au hasard - lors du redémarrage du jeu, du redémarrage de l'ordinateur ... mais cela valait la peine de changer n'importe quel paramètre de performance et il a disparu. Ensuite, vous pouvez modifier le paramètre et tout a continué à fonctionner parfaitement. Problème de fantôme.



De toute évidence, Sam n'était pas le seul problème. Lors du lancement d'autres jeux, il est apparu de la même manière, suggérant qu'il y avait un problème avec les pilotes. Mais le stattering ne dépendait pas du fabricant de votre GPU. Cela s'est passé même avec différentes API (OpenGL, DirectX 9, DirectX 11 ...). La seule chose qui restait en commun était que le stattering apparaissait ici et là sur certaines machines et scènes de jeu.



Avec la sortie de nouveaux jeux, ce problème a continué d'apparaître et de disparaître. Auparavant, cela n'affectait que certains utilisateurs, et tout était limité aux demandes du support technique pour modifier certains paramètres de performances - ce qui aidait parfois, et parfois non, on ne sait jamais.



Puis, tout d'un coup, une belle journée d'hiver au début de 2013, les gars de Croteam ont découvert un autre exemple de ce problème qui pouvait être reproduit de manière relativement cohérente à l'époque - cette fois à l'un des niveaux de Serious Sam 3. Ils ont tripoté cette scène pendant un long moment, jusqu'à ce que soudainement elle se lève. C'était si simple - pas étonnant que cela ait échappé aux regards du public pendant une décennie.



En changeant une seule option simple dans le moteur de jeu, ils ont pu résoudre ce problème. Cependant, il est immédiatement devenu clair que la solution exigerait en fait beaucoup plus de temps et d'efforts. Et pas seulement d'une équipe spécifique, mais de l'ensemble de l'écosystème de jeu: développeurs de pilotes GPU, spécialistes de la maintenance des API, fournisseurs de systèmes d'exploitation - tout le monde.





Que s'est-il passé tout ce temps



Voici à quoi cela ressemble lorsque le jeu ralentit même à 60 FPS. Vous auriez pu expérimenter quelque chose de similaire en jouant à n'importe quel jeu moderne, et la première chose que vous penseriez probablement est que le jeu n'est pas optimisé. Eh bien, revisitons cette théorie.



Si le jeu est "trop ​​lent", cela signifie qu'à certains moments, il ne sera pas en mesure de rendre une image assez rapidement et le moniteur devra à nouveau afficher l'image précédente. Par conséquent, lorsque nous tournons une vidéo à 60 images par seconde, elle devrait afficher des "images perdues" - lorsque l'image suivante n'a pas été affichée dans le temps, c'est pourquoi la même image a été affichée deux fois.



Cependant, cela ne se produit que lorsque vous lisez toute l'animation. Si vous le parcouriez image par image, vous ne trouverez aucun vide.





Comment est-ce possible?



Regardons cela de plus près. Vous trouverez ci-dessous une comparaison côte à côte entre une vidéo fluide idéale et une vidéo mise en scène:



image

Six images consécutives avec une synchronisation précise. Au-dessus - cadres correctement positionnés, en dessous - cadres avec statter.



Vous pouvez voir deux choses ici: d'abord, ils fonctionnent vraiment à la même vitesse: chaque fois qu'un nouveau cadre apparaît en haut (correct), un nouveau cadre apparaît en dessous (stattering). Deuxièmement, pour une raison quelconque, ils semblent se déplacer un peu différemment - il y a un «espace» notable au milieu de l'image qui fluctue entre une division temporelle plus grande et plus petite.



Les plus attentifs remarqueront peut-être un autre détail curieux: l'image du bas est censée être "plus lente" ... en fait elle est "en avance" sur la bonne. Etrange, non?



Si nous regardons plusieurs images consécutives et leur timing, nous pouvons voir autre chose d'intéressant: les deux premières images sont parfaitement synchronisées, mais dans la troisième image, l'arbre de la vidéo "plus lente" est nettement en avance sur son homologue dans le "correct" vidéo (entourée de rouge) ... Vous pouvez également remarquer que ce cadre a clairement pris plus de temps (entouré de jaune).



Attendez, attendez ... mais si la vidéo est «plus lente» et l'image «a pris plus de temps», comment peut-elle continuer?



Pour comprendre d'autres explications, vous devez d'abord comprendre comment les jeux modernes et autres applications 3D exécutent généralement l'animation et le rendu.





Une brève histoire de la synchronisation d'images



Il y a longtemps, dans une galaxie lointaine, très lointaine ... lorsque les développeurs créaient les premiers jeux vidéo, ils le faisaient généralement en fonction de la fréquence d'images exacte à laquelle l'affichage fonctionnait. Dans les régions NTSC, où les téléviseurs fonctionnent à 60 Hz, cela signifie 60 images par seconde, et dans les régions PAL / SECAM, où les téléviseurs fonctionnent à 50 Hz, 50 images par seconde.



La plupart des jeux étaient des concepts très simples fonctionnant sur du matériel fixe - généralement une console d'arcade ou un "micro-ordinateur domestique" bien connu comme le ZX Spectrum, C64, Atari ST, Amstrad CPC 464, Amiga, etc. Ainsi, créer et tester jeux pour une machine spécifique et une fréquence d'images spécifique, le développeur pouvait toujours être sûr à 100% que la fréquence d'images ne chuterait jamais nulle part.



Les vitesses des objets étaient également stockées dans des unités «du personnel». Ainsi, vous deviez savoir non pas combien de pixels par seconde le personnage se déplacerait, mais combien de pixels dans le cadre. Par exemple, dans Sonic The Hedgehog pour la Sega Genesis, cette vitesse est de 16 pixels par image. De nombreux jeux avaient même des versions distinctes pour les régions PAL et NTSC, où les animations étaient dessinées à la main spécifiquement pour 50 et 60 FPS, respectivement. En fait, travailler à n'importe quelle autre fréquence d'images était tout simplement impossible.



Et comme les jeux ont commencé à fonctionner sur des appareils plus variés au fil du temps, y compris des PC dotés d'un matériel en constante expansion et mis à niveau, il était impossible de savoir exactement à quelle fréquence d'images le jeu fonctionnerait. Ce fait a été aggravé par le fait que les jeux eux-mêmes sont devenus plus complexes et imprévisibles - cela est particulièrement visible dans les jeux 3D, où il peut y avoir de grandes différences dans la complexité de la scène, parfois même déterminée par les joueurs eux-mêmes. Par exemple, tout le monde aime tirer sur des piles de barils de carburant, provoquant une série d'explosions colorées ... et une baisse inévitable de la fréquence d'images. Mais comme c'est amusant, personne ne s'en soucie vraiment.



Par conséquent, il est difficile de prévoir combien de temps il faudra pour modéliser et rendre une image. (Veuillez noter que sur les consoles modernes, nous pouvons supposer que le matériel est fixe, mais les jeux eux-mêmes sont encore assez imprévisibles et complexes.)



Si vous ne pouvez pas être sûr à quelle fréquence d'images le jeu fonctionnera, vous devez mesurer les images de fréquence actuelles. et adapter en permanence la physique du jeu et la vitesse de l'animation pour celui-ci. Si une image prend 1 / 60e de seconde (16,67 ms) et que votre personnage tourne à 10 m / s, alors il se déplace de 1/6 de mètre dans chaque image. Mais si l'image commence soudainement à prendre 1/30 de seconde (33,33 ms), alors vous devez déjà déplacer le personnage de 1/3 mètre par image (deux fois "plus vite") pour qu'il continue à se déplacer au même niveau apparent la vitesse.



Comment l'organiser? En règle générale, le jeu mesure le temps au début des images adjacentes et calcule la différence. C'est une méthode assez simple, mais qui fonctionne très bien.



Au contraire, cela fonctionnait très bien avant. Dans les années 90, lorsque 35 FPS était considéré comme une vitesse, les gens en étaient plus que satisfaits. Mais à cette époque, les cartes vidéo n'étaient pas une partie aussi importante du PC, et le processeur central contrôlait tout ce qui se passait à l'écran. Si vous n'aviez pas d'accélérateur 3D, il dessinait même des objets tout seul. Ainsi, il savait exactement quand ils allaient frapper l'écran.





La situation aujourd'hui



Au fil du temps, des GPU de plus en plus sophistiqués ont commencé à apparaître, et ils sont inévitablement devenus de plus en plus "asynchrones". Cela signifie que lorsque le CPU dit au GPU de dessiner quelque chose à l'écran, le GPU stocke simplement cette commande dans un tampon afin que le CPU puisse continuer pendant le rendu du GPU. En fin de compte, cela conduit à une situation où le CPU indique au GPU quand la fin de la trame arrive, et le GPU, tout en stockant cela parmi ses données, ne le considère pas vraiment comme une priorité - après tout, il est toujours traitement de certaines des commandes émises précédemment ... Il affichera le cadre à l'écran uniquement lorsqu'il aura fait tout ce qu'il a été chargé auparavant.



Ainsi, lorsque le jeu tente de calculer le temps en soustrayant les horodatages au début de deux images consécutives, la pertinence de cela, franchement ... est hautement discutable. Revenons donc à notre exemple. Là, nous avons eu les images suivantes:



image

Six images consécutives avec un timing précis. La rangée du haut est correcte, la rangée du bas a un effet statter.



Dans les deux premières images, la durée de l'image est de 16,67 ms (ou 1/60 de seconde) et la caméra se déplace de la même quantité dans les majuscules et les minuscules, de sorte que les arbres sont synchronisés. Dans la troisième image (ci-dessous, avec stattering), le jeu a vu que le temps de trame est de 24,8 ms (soit plus de 1/60 de seconde) et pense donc que la fréquence d'images a chuté et se précipite pour rattraper le manquant ... seulement pour constater que dans l'image suivante, le temps n'est que de 10,7 ms, ce qui ralentit la caméra, et maintenant les arbres sont plus ou moins synchronisés à nouveau.



Ce qui se passe? Les temps de trame mesurés par un jeu fluctuent en raison de divers facteurs - en particulier sur un système multitâche occupé comme un PC. Par conséquent, à certains moments, le jeu suppose que la fréquence a chuté de 60 FPS et génère des images d'animation conçues pour une fréquence d'images inférieure. Mais en raison de la nature asynchrone du GPU, il revient toujours d'une manière ou d'une autre aux mêmes 60 images par seconde.



C'est hallucinant - une animation générée pour une fréquence d'images variable (fréquence cardiaque) affichée à la fréquence d'images fixe correcte réelle.



Donc, en substance, nous pouvons supposer qu'il n'y a pas de problème - tout se passe bien, le jeu ne le sait tout simplement pas.



Cela nous amène à ce dont nous avons parlé au début de l'article. Lorsque nous avons enfin compris la cause du problème (bien que nous sachions que c'est une illusion de problème - il n'y a vraiment pas de problème, non?), Nous pouvons appliquer la pilule magique suivante.



Quelle est cette pilule? Dans le Serious Engine, il est noté sim_fSyncRate = 60. En termes simples, cela signifie ceci: " ignorez complètement toutes ces manigances et prétendez que nous mesurons toujours 60 images stables par seconde ." Et cela fait que tout se passe bien - simplement parce que tout a fonctionné sans problème dès le début! La seule raison pour laquelle cette déclaration est survenue était que le timing était incorrect pour l'animation.



Et quoi, c'est tout?





La solution est donc aussi simple?



Malheureusement non. C'était juste sur les tests seulement. Si nous arrêtons de mesurer la fréquence d'images en conditions réelles et supposons simplement qu'elle est toujours égale à 60 FPS, alors quand elle descend en dessous de 60 - et sur un PC, elle chutera tôt ou tard pour une raison quelconque: programmes fonctionnant en arrière-plan, énergie conservation ou protection contre la surchauffe, qui sait - alors tout ralentira.



Donc, si nous mesurons le temps de trame, le statter se produit, et sinon, à un moment donné, tout peut ralentir. Et maintenant quoi?



La vraie solution serait de mesurer non pas l'heure de début / fin du rendu d'une image, mais l'heure à laquelle l'image est affichée à l'écran.



Mais comment le jeu peut-il savoir quand une image est réellement affichée à l'écran?



Pas question: pour le moment, c'est impossible.



Etrange mais vrai. On pourrait s'attendre à ce que ce soit une fonctionnalité essentielle de chaque API graphique. Mais non: ils ont subi des changements dans tous les autres aspects en plus de cela. Il n'y a aucun moyen de savoir avec certitude quand le cadre apparaîtra réellement à l'écran. Vous pouvez savoir quand le rendu est terminé. Mais ce n'est pas la même chose que le temps d'affichage.





Maintenant quoi?



Eh bien, ce n'est pas si mal. De nombreuses personnes travaillent activement à la mise en œuvre de la prise en charge d'une synchronisation de trame correcte pour différentes API. L'API Vulkan dispose déjà d'une extension appelée VK_GOOGLE_display_timing qui a fait ses preuves dans la mise en œuvre de ce concept, mais elle n'est disponible que pour un nombre limité d'appareils.



Des travaux sont en cours pour fournir des solutions similaires et meilleures, j'aimerais le croire déjà dans toutes les principales API graphiques. Quand? C'est difficile à dire, car le problème touche profondément divers sous-systèmes OS.



Cependant, nous espérons qu'il sera bientôt accessible au grand public.





Diverses mises en garde et autres détails



Nous supposerons que c'est la fin du texte principal. Les sections ci-dessous sont des «bonus» qui sont largement indépendants les uns des autres et de ce qui précède.



"Compositeur"



image

Est-ce un effet de verre dépoli? Ouais, c'est pourquoi nous devons avoir un compositeur. Assez important, n'est-ce pas?



Un concept appelé Compositing Window Manager , également connu sous le nom de compositeur, est impliqué dans les coulisses . C'est un système désormais présent dans tous les OS et qui permet aux fenêtres d'être transparentes, d'avoir un arrière-plan flou, des ombres, etc. Les compositeurs peuvent aller plus loin et afficher les fenêtres de votre programme en 3D. Pour ce faire, le compositeur prend le contrôle de la toute dernière partie de l'image et décide quoi en faire, juste avant qu'elle n'atteigne le moniteur.



Dans certains systèmes d'exploitation, le composeur peut être désactivé en mode plein écran. Mais ce n'est pas toujours possible, et même dans de tels cas - ne pouvons-nous pas exécuter le jeu en mode fenêtré?





Gestion de l'alimentation et de la chaleur VS complexité du rendu



Nous devons également prendre en compte le fait que les processeurs et les GPU modernes ne fonctionnent pas à une fréquence fixe, mais disposent tous deux de systèmes qui ajustent leur vitesse à la hausse et à la baisse en fonction de la charge et de la température actuelle. Ainsi, le jeu ne peut pas simplement supposer qu'ils auront la même vitesse d'une image à l'autre. D'un autre côté, le système d'exploitation et les pilotes ne peuvent pas s'attendre à ce que le jeu fasse la même quantité de travail à chaque image. Les systèmes de communication complexes entre deux parties doivent être conçus de manière à ce que tout cela soit pris en compte.





Ne pourrions-nous pas simplement ...



Impossible. :) Habituellement, la mesure du temps GPU est proposée comme alternative à l'affichage du temps. Mais cela ne prend pas en compte la présence d'un compositeur et le fait qu'aucun des minuteries de rendu GPU ne se synchronise réellement directement avec la mise à jour de l'affichage. Pour une animation parfaite, nous devons absolument savoir exactement quand l'image a été affichée, pas quand elle a fini le rendu.



All Articles