Introduction
Peu de choses gâchent autant l’expérience d’un joueur qu’une fréquence d’images saccadée. Bien que des défauts mineurs, comme un crénelage indésirable ou la main d’un joueur traversant occasionnellement un mur, soient gênants, ils ne perturbent pas l’ensemble du champ de vision du joueur et affectent rarement le gameplay. Les techniques traditionnelles permettant de maintenir une fréquence d’images fluide comprennent les ajustements du niveau de détail (LOD) appliqués à la géométrie, aux shaders et aux textures, ainsi que la limitation des budgets artistiques des scènes de sorte que l’image la plus défavorable puisse être rendue de manière fluide. Chacune de ces techniques présente des inconvénients. Les LOD de shaders et de géométrie sont certainement appropriés dans certains cas, mais ces techniques demandent un travail supplémentaire au développeur et à l’artiste. De plus, il est souvent difficile de masquer la transition d’un LOD au suivant lorsque des objets se trouvent au premier plan dans le champ de vision du joueur. Limiter le budget artistique d’un titre pour maintenir l’image la plus défavorable dans le budget peut finir par placer la barre très bas pour ce qui peut être affiché dans une image moyenne. Prenons par exemple la scène de la figure 1. Limiter le budget artistique de la scène pour tenir compte de la possibilité que plusieurs personnages occupent le panorama lors d’une phase très interactive du jeu pourrait restreindre inutilement la vue du vaste panorama lorsque peu de personnages, voire aucun, sont présents. Figure 1. Un scénario LOD typique
DXGIX_FRAME_STATISTICS et l’anatomie d’une image
Les figures suivantes présentent un exemple d’image typique représentée sur les chronologies du CPU, du GPU et de l’affichage. Figure 2. Une image unique sur trois chronologies
Remarque sur les fréquences d’horloge et les compteurs
De nombreux noms de membres de la structure DXGIX_FRAME_STATISTICS commencent par « GPUTime » ou « CPUTime ». Ces préfixes n’indiquent pas la chronologie à partir de laquelle les données sont collectées. Ils indiquent plutôt le domaine d’horloge échantillonné pour la statistique concernée. En fait, pour de nombreuses statistiques, nous renvoyons par commodité le temps échantillonné à la fois dans les domaines d’horloge du CPU et du GPU. Les préfixes « CPUTime » et « GPUTime » ont été volontairement omis dans la figure 2, car l’un ou l’autre domaine d’horloge peut être utilisé dans les calculs. La console XBOX One fait varier dynamiquement ses fréquences d’horloge afin d’économiser de l’énergie, mais lorsqu’un titre est en cours d’exécution, les fréquences d’horloge sont réglées au maximum afin de garantir les meilleures performances et la meilleure réactivité. La fréquence d’horloge maximale du CPU est de 1750 MHz, et la fréquence d’horloge de référence maximale du GPU (la fréquence de l’horloge d’horodatage) est de 100 MHz. Notez que l’horloge du cœur du GPU de la XBOX One fonctionne à 853 MHz, mais que la valeur de temps renvoyée par les statistiques d’image utilise le compteur à 100 MHz du GPU. Il est intéressant de noter que les horloges ne fonctionnent pas exactement à 1750 MHz ou 100 MHz. En effet, des organismes de réglementation, comme la Federal Communications Commission (FCC) aux États-Unis, exigent que les appareils n’émettent pas certaines fréquences. Pour que la console soit conforme à ces réglementations, une faible gigue doit être introduite. La fréquence exacte du CPU s’obtient facilement en appelant la fonction QueryPerformanceFrequency, et la fréquence exacte du compteur d’horodatage du GPU est définie par#define D3D11X_XBOX_GPU_TIMESTAMP_FREQUENCY.
Les statistiques d’image dont le nom de variable contient « Count » désignent soit un nombre de cycles d’horloge, soit le nombre d’occurrences d’un événement tel que la synchronisation verticale, par opposition à une valeur « Time » sur une chronologie.
Le Cookie (un pour chaque plan activé) est simplement transmis par le titre au moment de la présentation, ce qui permet d’associer une statistique donnée à un appel spécifique de la fonction DXGIXPresentArray.
Chronologie du CPU
Les commandes utilisées pour rendre une image sont générées sur le CPU. TimePresentCalled indique le moment où un titre a appelé DXGIXPresentArray sur le CPU, et copy indique le moment où l’image est ajoutée à la file d’attente de présentation interne. Vous pouvez soustraire la valeur TimeAddedToQueue d’une image de sa valeur TimePresentCalled pour obtenir la durée pendant laquelle Direct3D a limité le CPU. QueueLengthAddedToQueue indique à quel point la file d’attente de présentation était encombrée ou vide lorsque DXGIXPresentArray a été appelé. Ce membre enregistre le temps après l’attente (le cas échéant), et non avant. Le nombre total de cycles d’horloge du CPU consacrés à la construction d’une image (sans compter le temps de limitation par Direct3D) peut être calculé en soustrayant la valeur TimeAddedToQueue de l’image précédente de la valeur TimePresentCalled de l’image actuelle ; la valeur obtenue peut être convertie en secondes en la divisant par la fréquence du CPU.Chronologie du GPU
La chronologie du GPU représente la durée entre le moment où le GPU consomme les commandes envoyées par le CPU et le moment où il produit une image rendue. TimeFrameComplete indique le moment où le GPU termine l’image complète à permuter. Le temps total consacré au rendu de l’image (y compris le temps GPU passé à attendre la chaîne de permutation) peut être calculé en soustrayant TimeFrameComplete de la valeur correspondante de l’image précédente. La valeur obtenue peut être convertie en secondes en la divisant par la fréquence d’horodatage du GPU. GPUCountTitleUsed représente le nombre de cycles d’horloge du GPU utilisés par le titre au cours d’une image donnée. GPUCountSystemUsed représente le nombre de cycles d’horloge du GPU utilisés par le système au cours d’une image donnée pour les applications partagées ou les charges de travail NUI (interface utilisateur naturelle). Ces deux valeurs se comprennent mieux dans le contexte de la chronologie de l’affichage, en particulier la façon dont les charges de travail des applications partagées et NUI sont réparties en tranches de temps avec la charge de travail du titre.Chronologie de l’affichage
La dernière chronologie est celle de l’affichage. Quelles que soient la résolution et la fréquence de rafraîchissement réelles du téléviseur du joueur, le titre présente toujours sur un affichage virtuel 1080p (1920×1080 pixels à 59,94 Hz). Au début de chaque image se produit une synchronisation verticale, au cours de laquelle l’image suivante est envoyée à l’appareil ; TimeVSync indique le moment de cet événement. Le pointeur vers une image donnée est permuté pendant l’intervalle de suppression, sauf si un rendu en mode immédiat est effectué ; TimeFlip indique ce moment. VSyncCount représente l’« identifiant » de la synchronisation verticale sur laquelle l’image a été présentée. Il s’incrémente de un pour un rendu à 60 Hz, de deux pour un rendu à 30 Hz, et ainsi de suite, si aucune image n’est perdue. Enfin, une valeur PercentScanned est renvoyée ; elle indique le pourcentage de l’image qui avait été balayé lorsque le pilote a basculé vers l’image actuelle. Pour un rendu fixe à 30 Hz ou 60 Hz, cette valeur est toujours nulle, car aucun déchirement n’est autorisé. Pour un rendu en mode immédiat, ou pour un rendu avec une valeur PresentImmediateThreshold non nulle, cette valeur peut être non nulle.Remarque sur les tranches de temps
Au début de chaque image affichée, jusqu’à 8,5 % du GPU peut être réservé à l’usage du système. Notez qu’il s’agit d’une réduction par rapport aux 10 % réservés à l’usage du système lors du lancement de la XBOX One. Il existe quatre tranches de temps : NUI, Système exclusif, Système et titre partagés, et Titre exclusif.- Si la réservation GPU NUI est activée, 4,5 % fixes du GPU sont réservés à la charge de travail GPU NUI dans la tranche de temps NUI. Si la propriété système DisableKinectGpuReservation a la valeur true, la tranche de temps NUI est désactivée.
- Si l’indicateur de manifeste GpuAvailability est défini sur fixed, la tranche de temps Système exclusif est activée et 4 % fixes du GPU sont réservés ; la tranche de temps Système et titre partagés est alors désactivée.
- Si l’indicateur de manifeste GpuAvailability est défini sur variable, le titre doit répondre aux notifications d’événement ResourceAvailabilityChanged. Lorsque l’énumération ResourceAvailability est définie sur ResourceAvailability.FullWithExtendedSystemReserve en raison de la présence d’une application ancrée, la tranche de temps Système exclusif est fixée à 4 % et la tranche de temps Système et titre partagés est désactivée. Lorsque ResourceAvailability a la valeur ResourceAvailability.Full, la tranche de temps Système exclusif est désactivée, mais le système peut utiliser jusqu’à 3 % de la tranche de temps Système et titre partagés pour des notifications ou d’autres tâches.
- La tranche de temps Titre exclusif est utilisée uniquement pour le rendu du titre.
- Les tranches de temps NUI et Système exclusif commencent toutes les 60 Hz, au moment du VBlank. La tranche de temps NUI facultative vient en premier, suivie de la tranche de temps Système exclusif ou Système et titre partagés, puis enfin de la tranche de temps Titre exclusif. Par conséquent, une image GPU courte du titre qui commence peu après le début de la tranche de temps Titre exclusif ne peut être interrompue par aucun travail système ou NUI. En revanche, une image GPU plus longue (pour un rendu à 30 Hz), commençant juste avant le VBlank, peut potentiellement être interrompue par l’une des tranches de temps Système ou par la tranche de temps NUI, à deux moments distincts.
Compteurs d’utilisation par le titre et par le système
Voyons maintenant comment fonctionnent les compteurs de pourcentage d’utilisation. Le compteur GPUCountTitleUsed indique toujours, pour une image GPU donnée sur la chronologie du GPU, le nombre de cycles d’horloge du GPU consommés pendant le rendu de l’image du titre. Le compteur GPUCountSystemUsed indique, pour une image GPU donnée sur la chronologie du GPU, le nombre de cycles d’horloge du GPU réservés ou utilisés par le système ou NUI. Notez également que les compteurs de pourcentage d’utilisation ne sont pas identiques au compteur « % occupé » affiché dans PIX. Le compteur d’occupation du GPU dans PIX ne comptabilise pas le temps pendant lequel le GPU est bloqué en attente d’une barrière (fence). GPUCountTitleUsed et GPUCountSystemUsed incluent bien les temps de blocage, car nous mesurons le temps réellement consommé ou « utilisé », par opposition au temps pendant lequel le GPU est « occupé » à effectuer un travail utile.Utilisation des statistiques d’image pour la mise à l’échelle dynamique
Le code suivant présente quelques calculs simples utilisant les statistiques d’image, qui renvoient plusieurs valeurs intéressantes et utiles pour la résolution dynamique. C++

Mise à l’échelle de l’image
Une fois la mémoire tampon d’arrière-plan réduite, il reste à déterminer comment l’agrandir jusqu’à la résolution native 1080p que nous prévoyons de présenter. Nous recommandons de rendre l’interface utilisateur du titre sur un plan distinct en pleine résolution, qui est fusionné avec le rendu agrandi de la scène principale. Le rendu de l’interface utilisateur contient souvent du texte et d’autres éléments qui ne se mettent pas naturellement à l’échelle, et il est rarement coûteux en cycles d’horloge du GPU. L’utilisation d’un plan d’interface utilisateur entièrement distinct entraîne toutefois le coût de l’effacement de ce plan avant le rendu, ou au moins de l’effacement des sections du plan d’interface utilisateur qui changent d’une image à l’autre. Une autre façon de mettre à l’échelle l’image de la scène principale consiste simplement à utiliser un shader implémentant un agrandissement rapide et de haute qualité. Cette méthode a l’avantage de fonctionner sur toutes les plateformes, et elle permet le rendu recommandé des éléments d’interface utilisateur en pleine résolution après l’agrandissement, une fois le post-traitement ou l’anticrénelage de la scène principale terminé. Sur XBOX One, une façon potentiellement plus rapide et plus efficace de mettre à l’échelle l’image de la scène principale consiste à tirer parti du matériel personnalisé de la plateforme.Tirer parti du matériel d’agrandissement de la XBOX One
La XBOX One dispose d’un matériel spécifique à fonction fixe qui met à l’échelle et fusionne dynamiquement jusqu’à deux plans soumis par le titre. Ce matériel a été conçu intentionnellement pour ce scénario. Il prend en charge un agrandissement matériel entièrement programmable à dix prises horizontales et six prises verticales, ainsi que la fusion des deux plans présentés dans le matériel à fonction fixe. Deux plans de permutation sont présentés à l’aide de la fonction DXGIXPresentArray. De nombreuses options de filtrage sont disponibles, notamment des variantes de filtres sinc et Lanczos de haute qualité. Notez que plusieurs modifications ont été apportées au XDK XBOX One de février 2015 afin d’améliorer la qualité du scaler matériel. Plus précisément, une erreur de décalage d’un pixel a été corrigée, et des ajustements de filtre appropriés pour les décalages de pixels fractionnaires sont désormais effectués lors du redimensionnement de l’image.Alpha prémultiplié avec plusieurs plans de présentation
Dans un rendu à plan unique, les éléments de l’interface utilisateur utilisant la fusion alpha sont rendus de l’arrière vers l’avant avec une fusion alpha standard, comme le montre la figure 5.


DXGIXPresentArray utilise le plan matériel un pour désigner l’arrière-plan et le plan matériel zéro pour désigner le premier plan.
Dérivation
Considérons d’abord deux itérations de l’équation standard de fusion alpha et refactorisons-la sous une forme adaptée au nouveau plan d’interface utilisateur et au plan de la scène principale :
UI_COLOR à l’aide de la relation récursive correspondante suivante : 

(DST_COLOR<sub>0</sub>) est considérée comme déjà terminée, et (DST_COLOR<sub>0</sub>) est considérée comme l’image finale :

UI_COLOR<sub>n</sub>, SCENE_COLOR, FINAL_IMAGE et BLENDED_SCENE_COLOR<sub>n</sub> dans l’équation de fusion alpha d’origine, on obtient la solution suivante pour FINAL_IMAGE :


UI_ALPHA<sub>n</sub> ci-dessous). 
FINAL_IMAGE est défini comme égal à COMPOSITE_IMAGE. La résolution pour UI_ALPHA<sub>n</sub> donne le résultat suivant : 

Procédure
En rassemblant quelques résultats, on obtient la solution et la procédure finales :- Rendez la scène principale sur le plan matériel un de la manière habituelle.

- Effacez le plan matériel zéro en noir. Notez que vous devez effacer uniquement les parties du plan qui contiennent des éléments d’interface utilisateur dans chaque image. Ensuite, rendez les éléments d’interface utilisateur de la manière habituelle.
Accumulez les valeurs alpha de l’interface utilisateur dans le canal alpha du plan matériel un.
Le code suivant montre comment rendre le plan de l’interface utilisateur. C++
- Présentez les deux plans à l’aide du paramètre PresentImmediateThreshold de la fonction DXGIPresentArray. Le matériel gère la fusion finale lorsqu’il est configuré pour l’alpha prémultiplié à l’aide de l’indicateur DXGI_ALPHA_MODE.DXGI_ALPHA_MODE_PREMULTIPLIED.

Remarque sur le rendu immédiat et à seuil maximal lors de l’utilisation du scaler
Le matériel ne peut pas modifier les paramètres du scaler tant que l’affichage ne se trouve pas dans une zone de suppression verticale. En effet, les tampons de lignes et la configuration matérielle doivent rester cohérents au sein d’une image. Par conséquent, le rendu en mode immédiat revient par défaut à l’intervalle de présentation un et la valeur du paramètre PresentImmediateThreshold de la fonction DXGIPresentArray revient par défaut à zéro chaque fois que les paramètres du scaler (comme le type de filtre ou la taille de la mémoire tampon d’entrée) sont modifiés. Autrement dit, le rendu d’un titre ne peut pas présenter de déchirement sur l’image où une nouvelle taille d’entrée ou un nouveau type de filtre est sélectionné.Quand et comment mettre à l’échelle, meilleures pratiques et conseils issus du terrain
Les remarques et meilleures pratiques suivantes sont issues de l’implémentation de la résolution dynamique dans des exemples de code et dans des titres commercialisés.Mise à l’échelle horizontale à privilégier
Nous recommandons d’effectuer la mise à l’échelle uniquement dans le sens horizontal, pour plusieurs raisons. Tout d’abord, le scaler matériel de la XBOX One prend en charge davantage de prises dans le sens horizontal que dans le sens vertical, ce qui offre une mise à l’échelle horizontale de meilleure qualité. Ensuite, dans la plupart des titres, le mouvement naturel de la caméra varie surtout dans le sens horizontal, lorsque les joueurs balaient l’horizon du regard ; les changements horizontaux sont donc moins perceptibles.Quand ne pas mettre à l’échelle
Afin de réduire au minimum les artefacts visibles, nous recommandons de lier les mises à jour de la taille de la scène d’entrée aux moments où la scène est en mouvement. Cela inclut les moments où la caméra se déplace ou ceux où l’activité de la scène est intense. Nous recommandons également que les titres ne mettent pas continuellement leur résolution à l’échelle. Une fois qu’une résolution cible a été choisie et appliquée, une petite marge de performances doit être conservée afin que le titre puisse maintenir la résolution cible pendant une durée déterminée, pour éviter des visuels instables ou des changements de résolution excessifs.Réglage préventif des paramètres de mise à l’échelle
Toute logique du moteur de jeu servant à prédire la complexité de la scène peut être fournie à l’avance à la fonction qui détermine « l’ampleur de la mise à l’échelle ». Par exemple, si l’on sait que la solution de rendu est sur le point d’être sollicitée par l’apparition d’une grosse explosion ou d’un grand nombre de personnages, la résolution peut être abaissée de manière proactive. Un titre peut également prendre en compte la notification d’événement ResourceAvailabilityChanged dans sa décision de mise à l’échelle.Fréquences de rafraîchissement différentes pour l’interface utilisateur et la scène principale
Un titre à 60 Hz peut rendre le plan de l’interface utilisateur à 30 Hz afin d’économiser du temps GPU. Pour cela, il suffit de définir le champ UsePreviousBuffer sur true dans la structure DXGIX_PRESENTARRAY_PARAMETERS du plan de l’interface utilisateur, puis de ne remplir la mémoire tampon d’arrière-plan de l’interface utilisateur qu’une image sur deux. Un titre à 30 Hz peut également rendre le plan de l’interface utilisateur à 60 Hz si des mises à jour fluides de l’interface utilisateur sont nécessaires. Pour cela, il suffit de définir le champ UsePreviousBuffer sur true dans la structure DXGIX_PRESENTARRAY_PARAMETERS du plan de la scène principale.Marges de sécurité contre les images manquées
Un équilibre délicat doit être maintenu entre la part du GPU conservée en réserve pour gérer les pics inattendus d’utilisation et la part disponible pour l’utilisation moyenne globale. Si une trop grande part du GPU est réservée aux pics, la complexité moyenne des scènes en pâtit. Si la réserve est trop faible, davantage d’images seront perdues. Cet équilibre délicat est très spécifique à chaque jeu et peut même varier selon les scènes et les niveaux d’un même titre. Notez qu’en cas d’images perdues, il peut être préférable d’autoriser le déchirement de l’image suivante (à l’aide du paramètre PresentImmediateThreshold de la fonction DXGIPresentArray) plutôt que de bloquer. De plus, il peut être judicieux d’abaisser la résolution de plusieurs crans dès que possible en cas d’image perdue. Figure 7. Marge de sécurité de la charge de travail du GPU
Utilisation de DXGIXSetFrameNotification avec DXGIX_FRAME_STATISTICS
Plus tôt vous pouvez diagnostiquer votre fréquence d’images réelle, plus tôt vous pouvez agir et éviter les images perdues. Il est possible d’interroger les statistiques d’image à l’aide de la fonction DXGIXGetFrameStatistics avant que l’image ne soit entièrement balayée. Les titres peuvent également interroger les résultats à l’aide de DXGIGetFrameStatistics. Les données de chronologie sont valides lorsque les critères répertoriés dans le tableau suivant sont remplis.
Les titres peuvent utiliser la fonction DXGIXSetFrameNotification pour recevoir des notifications pour les événements répertoriés dans le tableau suivant.
En bref, il n’est pas nécessaire que l’image entière soit balayée pour que des données utiles à son sujet soient disponibles. Appeler la fonction DXGIXGetFrameStatistics tôt permet de réduire la marge de sécurité et de maximiser le nombre de pixels rendus pour une image moyenne.
