Partager via


Meilleures pratiques en matière de gestion des ressources

Les textures managées, également appelées gestion automatique des textures, sont disponibles dans DirectX depuis la version 6, avec plusieurs révisions et améliorations apportées dans les versions suivantes. À compter de la publication de l’API Direct3D 9, la gestion automatique des ressources inclut la prise en charge des textures, des tampons de vertex et des mémoires tampons d’index, le tout avec une interface partagée cohérente. En utilisant le gestionnaire de ressources Direct3D, les applications peuvent considérablement simplifier la gestion des situations de perte d’appareil et peuvent s’appuyer sur le système pour gérer une quantité raisonnable de sur-engagement des ressources de mémoire vidéo.

Les développeurs ont parfois des difficultés à utiliser des ressources managées, en partie en raison de la nature abstraite du système. Bien que de nombreux scénarios courants pour les ressources conviennent parfaitement aux ressources managées, certains cas fonctionnent mieux lors de l’utilisation de ressources non managées. Cet article décrit les meilleures pratiques pour traiter les ressources en général, le comportement des ressources managées et non managées, et fournit des détails sur la façon dont les ressources sont généralement gérées par le runtime et les pilotes.

Cet article traite des concepts suivants :

Mémoire vidéo

Pour que le système vidéo utilise une ressource, elle doit se trouver dans la mémoire accessible au GPU. La mémoire vidéo locale fournit les meilleures performances pour le GPU, et certaines ressources (telles que les cibles de rendu et les mémoires tampons de profondeur/gabarit) doivent être situées dans la mémoire vidéo locale. Avec l’avènement d’AGP, le GPU peut également accéder directement à une partie de la mémoire système. Cette zone de mémoire, appelée ouverture AGP, est appelée mémoire vidéo non locale et n’est pas disponible à d’autres fins. La mémoire vidéo non locale peut être lue et écrite par le processeur, qui n’a généralement pas d’accès hautes performances à la mémoire vidéo locale, et est donc idéal pour une utilisation en tant que ressource de mémoire partagée. Une chose essentielle à retenir à propos de la mémoire AGP est qu’elle, comme la mémoire vidéo locale, est invalidée dans les situations de perte d’appareil et que les ressources persistantes qui y sont situées doivent être restaurées.

Figure 1. Relation entre le GPU, le processeur, la ram vidéo et la RAM système

relation du gpu, du processeur, de la ram vidéo et de la ram système

Certaines solutions vidéo intégrées utilisent une architecture de mémoire unifiée (UMA), où main mémoire est adressable par tous les composants des systèmes. Direct3D prend en charge UMA sans nécessiter de modification de l’application, en utilisant les mêmes conseils que pour les configurations de mémoire vidéo locale. Pour ces systèmes, les ressources se trouvent toujours dans la mémoire système, et le pilote est chargé de s’assurer que les ressources fonctionnent comme dans une architecture plus traditionnelle tout en tirant parti des propriétés d’UMA et de tout comportement spécifique de l’implémentation matérielle.

Figure 2 : Le GPU et le processeur ont un accès égal à la RAM système dans une architecture de mémoire unifiée

gpu et processeur ont un accès égal à la ram système dans une architecture de mémoire unifiée

Ressources managées

La majorité de vos ressources doivent être créées en tant que ressources managées dans POOL_MANAGED. Toutes vos ressources seront créées dans la mémoire système, puis copiées selon les besoins dans la mémoire vidéo. Les situations de perte d’appareil sont gérées automatiquement à partir de la copie de mémoire système. Étant donné que toutes les ressources managées ne sont pas requises pour s’adapter à la mémoire vidéo en même temps, vous pouvez sur-valider la mémoire lorsqu’un petit ensemble de ressources de travail de la mémoire vidéo est tout ce qui est nécessaire pour le rendu dans n’importe quelle image donnée. Notez qu’il est probable que la majorité de la mémoire système de ce magasin de stockage sera paginée sur le disque au fil du temps, ce qui explique pourquoi l’opération de réinitialisation peut être lente en raison de la nécessité de pager de nouveau ces données pour restaurer la mémoire vidéo perdue.

Le runtime conserve un horodatage pour la dernière fois qu’une ressource est utilisée, et lorsqu’une allocation de mémoire vidéo échoue pour le chargement d’une ressource managée nécessaire, il libère des ressources basées sur ce timestamp de manière LRU. L’utilisation de SetPriority étant prioritaire sur l’horodatage, les ressources plus couramment utilisées doivent être définies sur une valeur de priorité plus élevée. Direct3D 9.0 dispose d’informations limitées sur la mémoire vidéo gérée par le pilote. Le runtime peut donc devoir supprimer plusieurs ressources pour créer une région suffisamment grande pour que l’allocation réussisse. Les priorités appropriées peuvent aider à éliminer les situations où quelque chose est expulsé et est à nouveau nécessaire peu de temps après. L’application peut également appeler EvictManagedResources pour forcer la suppression de toutes les ressources managées. Là encore, cette opération peut prendre du temps pour recharger toutes les ressources requises pour l’image suivante, mais elle est très utile pour les transitions de niveau où l’ensemble de travail change considérablement et pour supprimer la fragmentation de la mémoire vidéo.

Un nombre d’images est également conservé pour permettre au runtime de détecter si la ressource qu’il vient de choisir d’évincer a été utilisée au début de l’image actuelle, ce qui implique une situation de thrashing où plus de ressources sont en cours d’utilisation dans une seule image que ne le fera la mémoire vidéo. Cela déclenche la stratégie de remplacement pour basculer vers une mode MRU plutôt que LRU pour le reste de l’image, car cela a tendance à fonctionner légèrement mieux dans de telles conditions. Ce comportement de thrashing aura un impact significatif sur les performances de rendu. Notez que la notion de frame actuelle étant liée à EndScene, toute application qui utilise des ressources managées doit effectuer des appels réguliers à cette méthode.

Les développeurs qui cherchent à obtenir plus d’informations sur le comportement des ressources managées dans leur application peuvent utiliser la requête d’événement RESOURCEMANAGER via l’interface IDirect3DQuery9 . Cela fonctionne uniquement lors de l’utilisation des runtimes de débogage, de sorte que ces informations ne peuvent pas être dépendantes de l’application, mais elles fournissent des détails détaillés sur les ressources gérées par le runtime.

Bien que la compréhension du fonctionnement du gestionnaire de ressources puisse vous aider lors du paramétrage et du débogage de vos applications, il est important de ne pas lier votre application trop étroitement aux détails d’implémentation du ou des pilotes actuels. Les révisions du pilote ou les modifications apportées au matériel peuvent modifier considérablement le comportement, et les futures versions de Direct3D auront une gestion des ressources considérablement améliorée et sophistiquée.

ressources Driver-Managed

Les pilotes Direct3D sont libres d’implémenter la fonctionnalité de textures gérées par le pilote, indiquée par D3DCAPS2_CANMANAGERESOURCE, ce qui permet au pilote de gérer la gestion des ressources au lieu du runtime. Pour le (rare) pilote qui implémente cette fonctionnalité, le comportement exact du gestionnaire de ressources du pilote peut varier considérablement, et vous devez contacter le fournisseur du pilote pour plus d’informations sur la façon dont cela fonctionne pour son implémentation. Vous pouvez également vous assurer que le gestionnaire d’exécution est toujours utilisé à la place en spécifiant D3DCREATE_DISABLE_DRIVER_MANAGEMENT lors de la création de l’appareil.

Ressources par défaut

Bien que les ressources managées soient simples, efficaces et faciles à utiliser, il arrive que l’utilisation directe de la mémoire vidéo soit recommandée ou même nécessaire. Ces ressources sont créées dans la catégorie POOL_DEFAULT. L’utilisation de ces ressources entraîne des complications supplémentaires pour votre application. Le code est nécessaire pour faire face à la situation de perte d’appareil pour toutes les ressources POOL_DEFAULT, et les considérations relatives aux performances doivent être prises en compte lors de la copie de données dans celles-ci. Le fait de ne pas spécifier USAGE_WRITEONLY ou de rendre une cible de rendu verrouillable peut également imposer de graves pénalités de performances.

L’appel de Verrou sur une ressource POOL_DEFAULT risque plus de bloquer le GPU que d’utiliser une ressource POOL_MANAGED, sauf si vous utilisez certains indicateurs. Selon l’emplacement de la ressource, le pointeur retourné peut être vers une mémoire tampon de mémoire système temporaire, ou il peut s’agir d’un pointeur directement dans la mémoire AGP. S’il s’agit d’une mémoire tampon système temporaire, les données devront être transférées vers la mémoire vidéo après l’appel de déverrouillage . Si la ressource vidéo n’est pas en écriture seule, les données devront être transférées dans la mémoire tampon temporaire pendant le verrouillage. S’il s’agit d’une zone de mémoire AGP, les copies temporaires sont évitées, mais le comportement de cache requis peut ralentir les performances.

Veillez à écrire une ligne de cache complète de données dans n’importe quel pointeur vers la mémoire d’ouverture AGP pour éviter la pénalité de peignage d’écriture, ce qui induit un cycle de lecture-écriture, et l’accès séquentiel de la zone mémoire est préférable. Si votre application doit effectuer un accès aléatoire aux données lors de la création et que vous ne souhaitez pas utiliser une ressource managée pour la mémoire tampon, vous devez utiliser une copie de la mémoire système à la place. Une fois les données créées, vous pouvez ensuite diffuser le résultat dans la mémoire de ressource verrouillée pour éviter de payer une pénalité élevée pour l’opération de combinaison d’écriture du cache.

L’indicateur LOCK_NOOVERWRITE peut être utilisé pour ajouter des données de manière efficace pour certaines ressources, mais dans l’idéal, il est possible d’éviter plusieurs appels de verrouillage et de déverrouillage à la même ressource. Il est important d’utiliser correctement les différents indicateurs de verrouillage pour optimiser les performances, tout comme l’utilisation d’un modèle d’accès aux données compatible avec le cache lors du remplissage de la mémoire verrouillée.

Utilisation des ressources managées et des ressources par défaut

Le mélange d’allocations de ressources managées et de ressources POOL_DEFAULT peut entraîner une fragmentation de la mémoire vidéo et perturber la vue du runtime de la mémoire vidéo disponible pour les ressources managées. Dans l’idéal, vous devez créer toutes les ressources POOL_DEFAULT avant d’utiliser POOL_MANAGED ressources ou utiliser l’appel EvictManagedResources avant d’allouer des ressources non managées. N’oubliez pas que toutes les allocations effectuées à partir d’POOL_DEFAULT qui résident dans la mémoire vidéo lient la mémoire pendant la durée de vie de cette ressource qui n’est pas disponible pour une utilisation par le gestionnaire de ressources ou à toute autre fin.

Notez que contrairement aux versions précédentes de Direct3D, le runtime de la version 9 supprime automatiquement certaines ressources managées avant d’abandonner une allocation de ressources non managée ayant échoué en raison d’un manque de mémoire vidéo, mais cela peut potentiellement créer une fragmentation supplémentaire et même forcer une ressource à un emplacement sous-optimal (par exemple, avoir une texture statique dans la mémoire vidéo non locale). Là encore, il est préférable d’allouer toutes les ressources non managées requises à l’avance et avant d’utiliser des ressources managées.

Ressources dynamiques par défaut

Les données générées et mises à jour à une fréquence élevée n’ont pas besoin du stockage, car toutes les informations seront recréées lors de la restauration de l’appareil. Il est généralement préférable de créer de telles données dans POOL_DEFAULT, en spécifiant l’indicateur de USAGE_DYNAMIC, afin que le pilote puisse prendre des décisions d’optimisation lors du placement de la ressource, sachant qu’elle sera souvent mise à jour. Cela signifie généralement que vous placez la ressource dans la mémoire vidéo non locale. Par conséquent, l’accès au GPU est généralement beaucoup plus lent que la mémoire vidéo locale. Pour les architectures UMA, le pilote peut choisir un emplacement particulier pour les ressources dynamiques afin d’optimiser l’accès en écriture du processeur.

Cette utilisation est typique pour les solutions de skinning logicielles et les systèmes de particules basés sur l’UC qui remplissent les tampons de vertex/index, et l’indicateur LOCK_DISCARD garantit que les décrochages ne sont pas créés dans les cas où la ressource est toujours utilisée à partir de l’image précédente. L’utilisation d’une ressource managée dans ce cas met à jour une mémoire tampon système, qui sera ensuite copiée dans la mémoire vidéo, puis utilisée pour une ou deux images uniquement avant d’être remplacée. Pour les systèmes avec une mémoire vidéo non locale, la copie supplémentaire est éliminée par l’utilisation appropriée de ce modèle dynamique.

Les textures standard ne peuvent pas être verrouillées et peuvent uniquement être mises à jour via UpdateSurface ou UpdateTexture. Certains systèmes prennent en charge les textures dynamiques, qui peuvent être verrouillées, et utilisent le modèle LOCK_DISCARD, mais un bit de fonctionnalités (D3DCAPS2_DYNAMICTEXTURES) doit être vérifié avant d’utiliser ces ressources. Pour les textures hautement dynamiques (vidéo ou procédurale), votre application peut créer des POOL_DEFAULT et des ressources POOL_SYSTEMMEM correspondantes et gérer les mises à jour de la mémoire vidéo à l’aide de UpdateTexture. Pour les mises à jour très fréquentes et partielles, le paradigme UpdateTexture est probablement le meilleur choix.

Aussi utile que les ressources dynamiques puissent être, soyez prudent lors de la conception de systèmes qui reposent fortement sur la soumission dynamique. Les ressources statiques doivent être placées dans POOL_MANAGED pour garantir une bonne utilisation de la mémoire vidéo locale et pour utiliser plus efficacement la bande passante limitée du bus et de la mémoire main. Pour les ressources semi-statiques, vous constaterez peut-être que le coût d’un chargement occasionnel vers la mémoire vidéo locale est beaucoup moins élevé que le trafic de bus constant généré en les rendant dynamiques.

Ressources de mémoire système

Des ressources peuvent également être créées dans POOL_SYSTEMMEM. Bien qu’ils ne puissent pas être utilisés par le pipeline graphique, ils peuvent être utilisés comme sources pour mettre à jour POOL_DEFAULT ressources à l’aide de UpdateSurface ou UpdateTexture. Leur comportement de verrouillage est simple, bien que des décrochages puissent se produire s’ils sont utilisés par l’une des méthodes mentionnées précédemment.

Bien qu’elles résident dans la mémoire système, les ressources POOL_SYSTEMMEM sont limitées aux mêmes formats et fonctionnalités (comme la taille maximale) prises en charge par le pilote de périphérique. Le type de ressource POOL_SCRATCH est une autre forme de ressource de mémoire système qui peut utiliser tous les formats et fonctionnalités pris en charge par le runtime, mais qui n’est pas accessible par l’appareil. Les ressources scratch sont principalement destinées à être utilisées par les outils de contenu.

Figure 3. Ressources de mémoire dans la ram vidéo, l’ouverture AGP et la RAM système

ressources de mémoire dans la mémoire ram vidéo, l’ouverture agp et la mémoire ram système

Recommandations générales

L’obtention des détails de l’implémentation technique de la gestion des ressources contribuera grandement à atteindre vos objectifs de performances pour votre application. La planification de la façon dont les ressources sont présentées à Direct3D et la conception architecturale autour du chargement des données en temps opportun est une tâche plus compliquée. Nous recommandons un certain nombre de bonnes pratiques lors de la prise de ces décisions pour votre application :

  • Prétraiter toutes vos ressources. Il est pratique de s’appuyer sur une conversion au temps de chargement et une optimisation coûteuses pour vos ressources pendant le développement, mais cela représente une charge importante en matière de performances sur les ordinateurs de vos utilisateurs. Les ressources prétraitées sont plus rapides à charger, plus rapides à utiliser et vous donnent la possibilité d’effectuer des tâches hors ligne sophistiquées.
  • Évitez de créer de nombreuses ressources par image. Les interactions de pilote requises peuvent sérialiser le processeur et le GPU, et les opérations impliquées sont lourdes, car elles nécessitent souvent des transitions de noyau. Répartissez la création sur plusieurs images ou réutilisez des ressources sans les créer/les libérer. Dans l’idéal, vous devez attendre plusieurs images avant de verrouiller ou de libérer les ressources qui ont été récemment utilisées pour le rendu.
  • À la fin de l’image, veillez à dissocier tous les canaux de ressources (c’est-à-dire, les sources de flux, les étapes de texture et les index actuels). Cela permet de s’assurer que les références non activées aux ressources sont supprimées avant que le gestionnaire de ressources conserve les ressources résidentes qui ne sont en fait plus utilisées.
  • Pour les textures, utilisez des formats compressés (par exemple, DXTn) avec mip-maps et envisagez d’utiliser un atlas de textures. Celles-ci réduisent considérablement les besoins en bande passante et peuvent réduire la taille globale des ressources, ce qui les rend plus efficaces.
  • Pour la géométrie, utilisez la géométrie indexée, car cela permet de compresser les ressources de mémoire tampon de vertex, et le matériel vidéo moderne est fortement optimisé pour la réutilisation des sommets. En utilisant des nuanceurs de vertex programmables, vous pouvez compresser les informations de vertex et les développer pendant le traitement du vertex. Là encore, cela permet de réduire les besoins en bande passante et de rendre les ressources de mémoire tampon de vertex plus efficaces.
  • Évitez de sur-optimiser votre gestion des ressources. Les révisions ultérieures des pilotes, du matériel et du système d’exploitation peuvent potentiellement entraîner des problèmes de compatibilité si l’application est trop fortement adaptée à une combinaison particulière. Étant donné que la plupart des applications sont liées au processeur, la gestion coûteuse basée sur le processeur entraîne généralement plus de problèmes de performances qu’elle n’en résout.

Gestion des ressources

Appareils perdus

Optimisations des performances

Ressources de texture compressées

Requêtes

D3DUSAGE

D3DPOOL

D3DCREATE