Copie et accès aux données de ressources (Direct3D 10)
Il n’est plus nécessaire de considérer les ressources comme étant créées dans la mémoire vidéo ou dans la mémoire système. Ou si le runtime doit ou non gérer la mémoire. Grâce à l’architecture du nouveau WDDM (Windows Display Driver Model), les applications créent désormais des ressources Direct3D 10 avec différents indicateurs d’utilisation pour indiquer comment l’application envisage d’utiliser les données de ressource. Le nouveau modèle de pilote virtualise la mémoire utilisée par les ressources ; il devient alors de la responsabilité du système d’exploitation/du pilote/du gestionnaire de mémoire de placer les ressources dans la zone de mémoire la plus performante possible en fonction de l’utilisation attendue.
Le cas par défaut est que les ressources soient disponibles pour le GPU. Bien sûr, cela dit, il y a des moments où les données de ressources doivent être disponibles pour le processeur. La copie de données de ressources afin que le processeur approprié puisse y accéder sans affecter les performances nécessite une certaine connaissance du fonctionnement des méthodes d’API.
Copie de données de ressources
Les ressources sont créées en mémoire lorsque Direct3D exécute un appel Create. Ils peuvent être créés dans la mémoire vidéo, la mémoire système ou tout autre type de mémoire. Étant donné que le modèle de pilote WDDM virtualise cette mémoire, les applications n’ont plus besoin de suivre le type de ressources de mémoire dans lequel elles sont créées.
Dans l’idéal, toutes les ressources se trouvent dans la mémoire vidéo afin que le GPU puisse y accéder immédiatement. Toutefois, il est parfois nécessaire que l’UC lise les données de ressource ou que le GPU accède aux données de ressource dans laquelle l’UC a écrit. Direct3D 10 gère ces différents scénarios en demandant à l’application de spécifier une utilisation, puis propose plusieurs méthodes pour copier les données de ressources si nécessaire.
Selon la façon dont la ressource a été créée, il n’est pas toujours possible d’accéder directement aux données sous-jacentes. Cela peut signifier que les données de ressource doivent être copiées de la ressource source vers une autre ressource accessible par le processeur approprié. En termes de Direct3D 10, les ressources par défaut sont accessibles directement par le GPU, les ressources dynamiques et intermédiaires sont directement accessibles par le processeur.
Une fois qu’une ressource a été créée, son utilisation ne peut pas être modifiée. Au lieu de cela, copiez le contenu d’une ressource vers une autre ressource qui a été créée avec une utilisation différente. Direct3D 10 fournit cette fonctionnalité avec trois méthodes différentes. Les deux premières méthodes ( ID3D10Device::CopyResource et ID3D10Device::CopySubresourceRegion) sont conçues pour copier des données de ressource d’une ressource à une autre. La troisième méthode (ID3D10Device::UpdateSubresource) est conçue pour copier des données de la mémoire vers une ressource.
Il existe deux types main de ressources : mappable et non mappable. Les ressources créées avec des utilisations dynamiques ou intermédiaires sont mappables, tandis que les ressources créées avec des utilisations par défaut ou immuables ne sont pas mappables.
La copie de données entre les ressources non mappables est très rapide, car il s’agit du cas le plus courant et a été optimisée pour fonctionner correctement. Étant donné que ces ressources ne sont pas directement accessibles par le processeur, elles sont optimisées pour que le GPU puisse les manipuler rapidement.
La copie de données entre les ressources mappables est plus problématique, car les performances dépendent de l’utilisation avec laquelle la ressource a été créée. Par exemple, le GPU peut lire une ressource dynamique assez rapidement, mais ne peut pas y écrire, et le GPU ne peut pas lire ou écrire directement dans des ressources intermédiaires.
Les applications qui souhaitent copier des données d’une ressource avec une utilisation par défaut vers une ressource avec une utilisation intermédiaire (pour permettre au processeur de lire les données, c’est-à-dire le problème de lecture gpu) doivent le faire avec soin. Pour plus d’informations sur ce dernier cas, consultez Accès aux données de ressources.
Accès aux données de ressources
L’accès à une ressource nécessite un mappage de la ressource ; le mappage signifie essentiellement que l’application tente de donner à l’UC l’accès à la mémoire. Le processus de mappage d’une ressource afin que le processeur puisse accéder à la mémoire sous-jacente peut entraîner des goulots d’étranglement des performances et, pour cette raison, il faut veiller à savoir comment et quand effectuer cette tâche.
Les performances peuvent s’arrêter si l’application tente de mapper une ressource au mauvais moment. Si l’application tente d’accéder aux résultats d’une opération avant la fin de cette opération, un blocage du pipeline se produit.
L’exécution d’une opération de carte au mauvais moment peut entraîner une baisse importante des performances en forçant le GPU et le processeur à se synchroniser entre eux. Cette synchronisation se produit si l’application souhaite accéder à une ressource avant que le GPU ne termine sa copie dans une ressource que le processeur peut mapper.
Le processeur peut uniquement lire à partir des ressources créées avec l’indicateur D3D10_USAGE_STAGING. Étant donné que les ressources créées avec cet indicateur ne peuvent pas être définies en tant que sorties du pipeline, si le processeur souhaite lire les données d’une ressource générée par le GPU, les données doivent être copiées dans une ressource créée avec l’indicateur de préproduction. Pour ce faire, l’application peut utiliser les méthodes ID3D10Device::CopyResource ou ID3D10Device::CopySubresourceRegion pour copier le contenu d’une ressource vers une autre. L’application peut ensuite accéder à cette ressource en appelant la méthode Map appropriée. Lorsque l’accès à la ressource n’est plus nécessaire, l’application doit appeler la méthode Unmap correspondante. Par exemple, ID3D10Texture2D::Map et ID3D10Texture2D::Unmap. Les différentes méthodes map retournent des valeurs spécifiques en fonction des indicateurs d’entrée. Pour plus d’informations, consultez la section Remarques de carte.
Notes
Lorsque l’application appelle la méthode Map, elle reçoit un pointeur vers les données de ressource à accéder. Le runtime garantit que le pointeur a un alignement spécifique, en fonction du niveau de fonctionnalité. Pour les D3D_FEATURE_LEVEL_10_0 et les versions ultérieures, le pointeur est aligné sur 16 octets. Pour une valeur inférieure à D3D_FEATURE_LEVEL_10_0, le pointeur est aligné sur 4 octets. L’alignement de 16 octets permet à l’application d’effectuer des opérations optimisées pour SSE sur les données en mode natif, sans réalignement ni copie.
Considérations relatives aux performances
Il est préférable de considérer un PC comme une machine exécutant une architecture parallèle avec deux types de processeurs main : un ou plusieurs processeurs et un ou plusieurs GPU. Comme dans toute architecture parallèle, les meilleures performances sont obtenues lorsque chaque processeur est planifié avec suffisamment de tâches pour l’empêcher d’être inactif et lorsque le travail d’un processeur n’attend pas le travail d’un autre.
Le pire scénario pour le parallélisme GPU/PROCESSEUR est la nécessité de forcer un processeur à attendre les résultats du travail effectué par un autre. Direct3D 10 tente de supprimer ce coût en rendant asynchrones les méthodes ID3D10Device::CopyResource et ID3D10Device::CopySubresourceRegion ; la copie n’a pas nécessairement été exécutée au moment où la méthode retourne. L’avantage est que l’application ne paie pas le coût de performances de la copie réelle des données tant que le processeur n’accède pas aux données, c’est-à-dire lorsque Map est appelé. Si la méthode Map est appelée après la copie des données, aucune perte de performances ne se produit. En revanche, si la méthode Map est appelée avant que les données n’ont été copiées, un blocage de pipeline se produit.
Les appels asynchrones dans Direct3D 10 (qui sont la grande majorité des méthodes, en particulier les appels de rendu) sont stockés dans ce qu’on appelle une mémoire tampon de commande. Cette mémoire tampon est interne au pilote graphique et est utilisée pour effectuer des appels par lots au matériel sous-jacent afin que le passage coûteux du mode utilisateur au mode noyau se produise aussi rarement que possible dans Microsoft Windows.
La mémoire tampon de commande est vidée, ce qui entraîne un changement de mode utilisateur/noyau, dans l’une des quatre situations suivantes.
- Present est appelé.
- ID3D10Device::Flush est appelé.
- La mémoire tampon de commande est pleine ; sa taille est dynamique et est contrôlée par le système d’exploitation et le pilote graphique.
- Le processeur nécessite l’accès aux résultats d’une commande en attente d’exécution dans la mémoire tampon de commande.
Parmi les quatre situations ci-dessus, le numéro 4 est le plus critique pour les performances. Si l’application émet un appel ID3D10Device::CopyResource ou ID3D10Device::CopySubresourceRegion , cet appel est mis en file d’attente dans la mémoire tampon de commande. Si l’application tente ensuite de mapper la ressource intermédiaire qui était la cible de l’appel de copie avant que la mémoire tampon de commande ait été vidée, un blocage du pipeline se produit car non seulement l’appel de méthode Copy doit s’exécuter, mais toutes les autres commandes mises en mémoire tampon dans la mémoire tampon de commande doivent également s’exécuter. Cela entraînera la synchronisation du GPU et du processeur, car le processeur attendra d’accéder à la ressource intermédiaire pendant que le GPU vide la mémoire tampon de commande et remplit enfin la ressource dont le processeur a besoin. Une fois que le GPU a terminé la copie, le processeur commence à accéder à la ressource de préproduction, mais pendant ce temps, le GPU reste inactif.
Le fait d’effectuer cette opération fréquemment au moment de l’exécution a pour conséquence de dégrader gravement les performances. Pour cette raison, le mappage des ressources créées avec l’utilisation par défaut doit être effectué avec soin. L’application doit attendre suffisamment longtemps pour que la mémoire tampon de commandes soit vidée et donc que toutes ces commandes se terminent avant d’essayer de mapper la ressource intermédiaire correspondante. Combien de temps l’application doit-elle attendre ? Au moins deux images, car cela permet d’exploiter au maximum le parallélisme entre le ou les processeurs et le GPU. Le fonctionnement du GPU est que pendant que l’application traite l’image N en envoyant des appels à la mémoire tampon de commande, le GPU est occupé à exécuter les appels de la trame précédente, N-1.
Par conséquent, si une application souhaite mapper une ressource qui provient de la mémoire vidéo et appelle ID3D10Device::CopyResource ou ID3D10Device::CopySubresourceRegion à l’image N, cet appel commence réellement à s’exécuter à l’image N+1, lorsque l’application envoie des appels pour l’image suivante. La copie doit être terminée lorsque l’application traite le cadre N+2.
Frame | État du PROCESSEUR/GPU |
---|---|
N |
|
N+1 |
|
N+2 |
|
N+3 |
|
N+4 | ... |
Rubriques connexes