Les solutions de streaming : Streaming local Steam et Nvidia GameStream en test

Publié le 26/07/2016 par
Imprimer

Préalable : Écrans et moteurs des jeux]

Pour bien comprendre le fonctionnement des logiciels de streaming, nous allons revenir sur les deux prochaines pages sur la manière dont fonctionne le rendu des jeux, et ce plus particulièrement sous Windows ou le fonctionnement avec DirectX est assez particulier, et s'est grandement complexifié depuis quelques années. En commençant par la fin, à savoir l'écran ! Certains détails peuvent paraître un peu pointus mais ils vous permettront de comprendre le fonctionnement des solutions que nous avons testées.

Un écran à rafraîchissement fixe

Les problèmes autour de l'affichage viennent du fait que les écrans fonctionnent à une fréquence de rafraîchissement fixe. Pour tous les exemples qui suivent, nous allons prendre le cas d'un écran classique ayant une résolution de 1920 par 1080 pixels pour un taux de rafraîchissement de 60 Hz.

Notez pour être complet qu'il existe également désormais des moniteurs avec un taux de rafraîchissement variable, dans un premier temps avec le G-Sync propriétaire à Nvidia, puis désormais avec l'Adaptive Sync, porté par AMD mais qui fait désormais partie de la spécification Display Port. Leurs particularités ne s'appliquent pas au streaming qui reste sur un taux d'image constant, comme nous allons le voir. Nous mettrons donc de côté ces solutions mais sachez qu'elles existent.

En pratique quand l'on parle de 60 Hz, on indique que l'écran reçoit 60 images par secondes en provenance de la carte graphique, et plus précisément une image toutes les 16.66 millisecondes. Une fois l'image reçue, l'écran commence à rafraîchir son panneau avec la nouvelle image, toujours à la même cadence fixe de 16.66 millisecondes.

Si l'on parle de "commencer à rafraîchir", c'est que cette étape en elle même prend du temps. Dans le cas d'un écran LCD, les pixels sont indépendants et seuls ceux qui ont changés depuis l'image précédente changent de couleur. Contrairement aux écrans cathodiques d'antan, il n'y a plus (hors modèles spécifiques) d'effet de balayage, même si l'on peut parfois percevoir un scintillement sur certains modèles. En pratique, ce que l'on perçoit est le scintillement du rétroéclairage (une source lumineuse placée derrière les LCD, souvent à base de néons ou de leds) qui, lorsque la luminosité n'est pas au maximum, peut être contrôlé par un PWM (qui allume et éteint rapidement le rétro éclairage pour réduire la luminosité "perçue").

Si un pixel était blanc sur l'image précédente et passe au noir sur la suivante, le pixel va amorcer sa migration, passant par une série de gris avant d'arriver au noir, une étape qui prend selon les écrans quelques millisecondes, vous pouvez le voir ici en pratique :


Sur notre écran de test, il faut compter environ 3,7 millisecondes pour voir l'écran passer du noir au blanc, on le voit ici augmenter progressivement sa luminosité avant d'arriver au niveau de blanc. Il est annoncé par le constructeur avec un "temps de réponse" de 2 millisecondes

Qui plus est, toutes les transitions de couleurs ne vont pas s'effectuer exactement à la même vitesse ! Passer du blanc au noir, le grand écart, n'est pas forcément le plus lent sur les écrans modernes, les transitions de couleurs éloignées mais non absolues (orange clair a violet foncé au hasard, ou plus conventionnellement gris clair vers gris foncé) pouvant être plus longues. Tout dépend évidemment des technologies utilisés par les moniteurs.

En pratique, si la réactivité de l'écran joue évidemment son rôle sur la latence que l'on va percevoir dans les jeux, ce n'est qu'une toute petite partie de la chaîne. Le taux de rafraîchissement va par contre avoir un impact décuplé par la suite.

Un moteur de jeu : CPU + GPU

Le lien entre l'écran et le PC reste bien entendu la carte graphique. C'est elle qui évidemment est reliée à l'écran, et qui a pour tache d'envoyer les images dans la bonne résolution, et au taux de rafraîchissement supporté par l'écran.

Mais en pratique la carte graphique n'est pas le centre du PC, d'un point de vue technique elle reste un périphérique asservi et piloté par le processeur qui reste le centre de tout. En pratique la carte graphique dispose d'une grande autonomie, mais elle doit être dirigée par un jeu et son moteur graphique.

Qu'est ce qu'un moteur de jeu donc ? Il s'agit d'une application tournant sur le processeur, qui va remplir plusieurs tâches dans une boucle infinie :

  • Récupérer les informations récentes (clavier, souris, réseau, etc...)
  • Mettre à jour le modèle de simulation du jeu en fonction des informations et du temps passé depuis la dernière image calculée
  • Effectuer les tâches nécessaires pour que le GPU puisse calculer la prochaine image
  • Donner l'ordre à la carte graphique de calculer une image, puis de "l'afficher"

La première parait évidente. En pratique au début de la boucle, le moteur interroge et récupère les derniers événements des joueurs, comme par exemple vos pressions éventuelles sur le clavier, la souris ou un pad, et éventuellement des informations en provenance d'un serveur dans le cas d'un jeu réseau.

Ces informations vont servir à mettre à jour le "modèle" de simulation du jeu, c'est la seconde étape. Si vous avez appuyé sur le bouton du frein dans un jeu de voiture par exemple, la vitesse du véhicule va devoir diminuer (de quelle manière, dans quelle proportions est la tâche du moteur qui regarde par exemple la force de pression sur un bouton analogique, ou qui peut prendre en compte le temps qui s'est écoulé depuis que vous avez commencer à appuyer sur le frein, etc...). L'autre étape primordiale est de calculer le temps qui s'est écoulé depuis la dernière image calculée.

Pour commencer, nous allons imaginer le cas où la synchronisation verticale est active, c'est à dire que l'on ne va pas tenter de calculer plus d'images que l'écran ne peut en afficher. Dans un monde parfait, il s'est donc écoulé exactement 16.66 millisecondes. Pendant ces 16.66 millisecondes, notre voiture s'est déplacée. En fonction de la vitesse, de la direction de la voiture, et éventuellement d'autres facteurs, le moteur calcule le nouvel emplacement du véhicule sur la piste.

Outre la modélisation interne au moteur (comme la vitesse ou la position), il faut mettre à jour la représentation géométrique. Notre voiture s'est déplacée, on connaît sa position, mais il faut également déplacer les triangles qui la composent. On va également déplacer en avant la caméra qui a avancé. Une jauge indiquant les freins va aussi changer de couleur et l'on pourrait même voir apparaître de la fumée autour des roues suite à un blocage de roues (ce qui nécessite de rajouter des effets).

Le travail préparatoire reste effectué sur le CPU avant que l'ordre final (des buffers de commandes) soit envoyé au GPU pour qu'il calcule l'image. Dans un monde idéal, il est obligatoire de limiter au maximum les interactions entre le CPU et le GPU qui doivent être capables d'effectuer leurs tâches respectives de manière indépendante. Si la synchronisation verticale est appliquée, DirectX va "bloquer" le moteur CPU pour s'assurer que la boucle ne soit pas exécutée plus de fois que nécessaire.

Si l'on tente de résumer cela par un schéma, on retrouve quelque chose qui ressemble à cela, nous prenons le cas de trois images successives (A, B et C) et nous regardons les étapes par lesquelles elles passent :

Vous retrouvez sur cette frise le temps qui s'écoule en millisecondes, chaque période de 16.66 ms étant matérialisée. Parlons d'abord des événements sur le pad dans notre cas du jeu de voiture (ou clavier/souris). Il y a de fortes chances pour que vos appuis sur le pad ne soient pas alignés parfaitement avec le rythme imposé par votre écran. Quand le moteur (en bleu) va récupérer les dernières informations, un temps s'est écoulé depuis le moment ou vous avez appuyé sur le bouton, et il est par définition variable et incompressible. Il prend en compte la transmission de l'information par le pad (via éventuellement un récepteur sans fil, puis une couche USB, jusque aux API DirectInput/Xinput de Microsoft) jusqu'au moteur.

Le moteur travaille ensuite à la préparation de l'image, pour l'exemple le temps reste limité à 8 millisecondes. Dans un monde idéal, un développeur de jeu voudra maximiser au mieux l'utilisation du processeur en utilisant au mieux l'intervalle qui lui est donné (16.66 ms ici) tout en s'assurant de rester impérativement en dessous ! Passer au dessus décalerait tout.

Ensuite, le GPU va enfin calculer l'image a partir des commandes qu'il a reçu, un travail qu'il fait quasiment seul (piloté plus ou moins par le driver graphique qu'on mettra de côté, en fonction de la version de DirectX utilisée), on le voit en vert. Il y aura toujours un décalage entre le moment ou le moteur va commencer à préparer l'image et celui ou le GPU va la calculer : c'est normal. Pendant que l'image A est calculée par le GPU, l'image B est préparée par le CPU, et ainsi de suite. Le rendu de l'image dans notre exemple prend 15 millisecondes. La encore, on reste sous le couperet des 16,6 ms.

Le rendu de l'image s'effectue dans un buffer et l'image "complète" est disponible à la fin du rendu (soit 33ms tout de même après que le CPU ait commencé à travailler !), prête à être affichée... ou presque !

Vos réactions

Top articles