Je sais que le titre est un peu vague mais il est difficile de décrire ce que je recherche vraiment, mais voilà.
En ce qui concerne le rendu du processeur, les performances sont généralement faciles à estimer et simples, mais en ce qui concerne le GPU en raison de mon manque d'informations techniques de base, je ne sais rien. J'utilise XNA donc ce serait bien si la théorie pouvait être liée à ça.
Donc, ce que je veux vraiment savoir, c'est ce qui se passe quand et où (CPU / GPU) lorsque vous effectuez des actions de dessin spécifiques? Qu'est-ce qu'un lot? Quelle influence les effets, les projections, etc. ont-ils? Les données sont-elles conservées sur la carte graphique ou sont-elles transférées à chaque étape? Quand il est question de bande passante, parlez-vous d'une bande passante interne de carte graphique, ou du pipeline du CPU au GPU?
Remarque: Je ne recherche pas réellement d'informations sur la façon dont le processus de dessin se déroule, c'est l'affaire du GPU, je m'intéresse à tous les frais généraux qui précèdent.
J'aimerais comprendre ce qui se passe quand je fais l'action X, pour adapter mes architectures et mes pratiques à cela.
Tous les articles (éventuellement avec des exemples de code), informations, liens, tutoriels qui donnent plus d'informations sur la façon d'écrire de meilleurs jeux sont très appréciés. Merci :)
Réponses:
J'aime penser la performance en termes de " limites ". C'est un moyen pratique de conceptualiser un système interconnecté assez compliqué. Lorsque vous rencontrez un problème de performances, vous posez la question: "Quelles limites suis-je en train d'atteindre?" (Ou: "Suis-je lié au CPU / GPU?")
Vous pouvez le décomposer en plusieurs niveaux. Au plus haut niveau, vous avez le CPU et le GPU. Vous pouvez être lié au processeur (le GPU reste inactif en attente du processeur) ou lié au GPU (le processeur attend le GPU). Voici un bon article de blog sur le sujet.
Vous pouvez le décomposer davantage. Côté CPU , vous pouvez utiliser tous vos cycles sur des données déjà dans le cache CPU. Ou vous pouvez être limité en mémoire , laissant le processeur inactif en attendant que les données arrivent de la mémoire principale ( alors optimisez la disposition de vos données ). Vous pouvez le décomposer encore plus.
(Bien que je fasse un large aperçu des performances concernant XNA, je soulignerai qu'une allocation d'un type de référence (
class
passtruct
), bien que normalement bon marché, pourrait déclencher le garbage collector, qui brûlera beaucoup de cycles - en particulier sur Xbox 360 . Voir ici pour plus de détails).Côté GPU , je vais commencer par vous signaler cet excellent article de blog qui contient beaucoup de détails. Si vous voulez un niveau fou de détails sur le pipeline, lisez cette série de billets de blog . (En voici une plus simple ).
Pour le dire ici simplement, certains des plus grands sont les suivants: " limite de remplissage " (combien de pixels vous pouvez écrire dans le backbuffer - souvent combien d'overdraw vous pouvez avoir), " limite de shader " (la complexité de vos shaders et la quantité de données que vous pouvez parcourir), " limite de texture-fetch / texture-bandwidth " (quantité de données de texture auxquelles vous pouvez accéder).
Et, maintenant, nous arrivons au grand - c'est ce que vous demandez vraiment - où le CPU et le GPU doivent interagir (via les différentes API et pilotes). En gros, il y a la " limite de lot " et la " bande passante ". (Notez que la première partie de la série que j'ai mentionnée précédemment va dans de nombreux détails.)
Mais, fondamentalement, un lot ( comme vous le savez déjà ) se produit chaque fois que vous appelez l'une des
GraphicsDevice.Draw*
fonctions (ou une partie de XNA, par exempleSpriteBatch
, le fait pour vous). Comme vous l'avez sans doute déjà lu, vous en obtenez quelques milliers * par image. Il s'agit d'une limite de CPU - elle est donc en concurrence avec votre autre utilisation de CPU. Il s'agit essentiellement du pilote qui compile tout ce que vous lui avez dit de dessiner et l'envoie au GPU.Et puis il y a la bande passante du GPU. C'est la quantité de données brutes que vous pouvez y transférer. Cela inclut toutes les informations d'état qui vont avec les lots - tout, de la définition de l'état de rendu et des constantes / paramètres de shader (qui incluent des choses comme les matrices de monde / vue / projet), aux sommets lors de l'utilisation des
DrawUser*
fonctions. Il comprend également tous les appels versSetData
etGetData
sur les textures, les tampons de vertex, etc.À ce stade, je dois dire que tout ce que vous pouvez appeler
SetData
(textures, tampons de vertex et d'index, etc.), ainsi queEffect
s - reste dans la mémoire du GPU. Il n'est pas constamment renvoyé au GPU. Une commande draw qui fait référence à ces données est simplement envoyée avec un pointeur vers ces données.(Aussi: vous ne pouvez envoyer des commandes de dessin qu'à partir du thread principal, mais vous pouvez
SetData
le faire sur n'importe quel thread.)XNA complique les choses un peu avec ses classes de rendre publiques (
BlendState
,DepthStencilState
, etc.). Ces données d'état sont envoyées par appel de tirage (dans chaque lot). Je ne suis pas sûr à 100%, mais j'ai l'impression qu'il est envoyé paresseusement (il n'envoie que l'état qui change). Quoi qu'il en soit, les changements d'état sont bon marché au point d'être gratuits, par rapport au coût d'un lot.Enfin, la dernière chose à mentionner est le pipeline interne du GPU . Vous ne voulez pas le forcer à vider en écrivant sur les données qu'il doit encore lire ou en lisant les données qu'il doit encore écrire. Un vidage de pipeline signifie qu'il attend la fin des opérations pour que tout soit dans un état cohérent lors de l'accès aux données.
Les deux cas particuliers à surveiller sont les suivants: Faire appel
GetData
à quelque chose de dynamique - en particulier àRenderTarget2D
celui sur lequel le GPU peut écrire. C'est extrêmement mauvais pour les performances - ne le faites pas.L'autre cas fait appel
SetData
à des tampons vertex / index. Si vous devez le faire souvent, utilisez unDynamicVertexBuffer
(aussiDynamicIndexBuffer
). Ceux-ci permettent au GPU de savoir qu'ils changeront souvent et de faire de la magie de mise en mémoire tampon en interne pour éviter la vidange du pipeline.(Notez également que les tampons dynamiques sont plus rapides que les
DrawUser*
méthodes - mais ils doivent être préalloués à la taille maximale requise.)... Et c'est à peu près tout ce que je sais sur les performances XNA :)
la source