CPU - Flux de données de la mémoire GPU [fermé]

16

Je suis un programmeur graphique débutant et je me suis demandé récemment - comment les données du modèle (maillages et matériaux) circulent-elles de l'application (mémoire CPU) vers la carte graphique (mémoire GPU?)? Supposons que j'ai un modèle statique (par exemple un bâtiment) que je charge et configure une fois et que je ne change pas pendant la durée de vie de l'application.

  • Ses données sont-elles envoyées à la mémoire du GPU une seule fois et restent-elles là pour toujours?
  • Lorsque le modèle est réellement rendu à chaque image, les processeurs GPU doivent-ils récupérer ses données à chaque fois dans la mémoire GPU? Ce que je veux dire, c'est que si j'avais 2 modèles rendus plusieurs fois chacun, cela aurait-il de l'importance si je rendais d'abord le premier plusieurs fois puis le second plusieurs fois ou si je rendais le premier une seule fois, le second une seule fois et continué à l'entrelacer comme ça? Je pourrais appeler cette question "flux de données GPU interne" dans ce sens.
  • De toute évidence, les cartes graphiques ont une mémoire RAM limitée - quand elles ne peuvent pas contenir toutes les données de modèle nécessaires pour le rendu d'une image, je suppose qu'elle continue de les récupérer (une partie) de la RAM du processeur à chaque image, est-ce correct?

Je sais qu'il y a beaucoup de livres et d'autres choses à ce sujet sur Internet, mais peut-être avez-vous des directives générales rapides sur la façon de gérer ce flux de données (quand envoyer quoi et combien, quand et comment rendre)?

Edit: j'ai oublié de faire une distinction: il y a l' envoi des données au GPU et il y a la définition / liaison des tampons comme courant . Ce dernier provoque-t-il un flux de données?

Edit2: Après avoir lu le post de Raxvan, je voudrais distinguer quelques actions:

  • création de tampon avec initialisation (comme il l'a dit, je peux stocker les données dans un ram CPU ou un GPU)
  • mise à jour des données du tampon (ce qui, à mon avis, est simple lorsque les données sont conservées dans le RAM du processeur et nécessite une récupération du GPU vers le RAM du processeur (puis retour) lorsqu'elle est conservée dans le RAM du GPU)
  • lier le tampon comme actif (est-ce juste un moyen de dire à l'API que je veux que ce tampon soit rendu lors du prochain appel de dessin et qu'il ne fasse rien par lui - même ?)
  • Appel d'appel API (ici, j'aimerais avoir de vos nouvelles ce qui se passe réellement là-bas)
NPS
la source
Je ne suis en aucun cas un expert, mais si vous utilisez OpenGL moderne (c'est-à-dire pas immédiat) avec des VAO et des VBO, les données sont envoyées au GPU et stockées dans la VRAM chaque fois que vous utilisez l'une des commandes de la famille glBuffer. Ensuite, chaque fois que vous le dessinez, les sommets pertinents sont extraits de la VRAM et rendus. S'il s'agit d'un modèle qui bouge, vous avez tendance à le stocker de manière statique et à utiliser des matrices pour passer de l'espace objet à l'espace monde / caméra. Quant au dernier point, je n'ai aucune idée de ce qui se passe si vous manquez de RAM. Je suppose que si vous manquez de VRAM, les données ne sont tout simplement pas envoyées, éventuellement avec un code d'erreur.
Polar
@Polar - pas exactement. GL ne spécifie pas réellement dans quelle mémoire un objet tampon est stocké et est même libre de le déplacer lors de l'exécution en fonction du modèle d'utilisation. GL4.4 répond un peu à cela, mais note qu'en fin de compte, le mieux qu'il puisse fournir est "une de ces astuces idiotes"; voir opengl.org/registry/specs/ARB/buffer_storage.txt et en particulier les numéros 2 et 9.
Maximus Minimus
1
@JimmyShelter Ah, merci - ce serait bien si nous avions moins de "ces trucs idiots" et une spécification plus concrète.
Polar
@Polar - ce qui est ennuyeux, c'est que ARB_buffer_storage aurait pu éviter d'inclure un autre indice, mais les concepteurs ont raté l'occasion. Eh bien, peut-être que 4,5 finira par faire les choses.
Maximus Minimus
2
Veuillez ne pas modifier vos questions pour "répondre" aux réponses. Postez plutôt une nouvelle question.

Réponses:

12

Ses données sont-elles envoyées à la mémoire du GPU une seule fois et restent-elles là pour toujours?

Habituellement oui, mais le pilote est libre de faire ce qui est "optimal", les données peuvent être stockées sur VRAM ou RAM ou peuvent simplement être mises en cache ici est un atricule qui explique ce qui se passe réellement avec le flux VBO .

Par exemple, s'il a été marqué comme tampon openGL dynamique (par exemple VBO), il est plus susceptible d'être stocké dans la RAM. Le GPU utilise l'accès direct à la mémoire (DMA) pour accéder directement au ram sans l'intervention du CPU, ceci est contrôlé par le contrôleur DMA dans la carte graphique et le pilote graphique et est exécuté en mode noyau.

Lorsque le modèle est réellement rendu à chaque image, les processeurs GPU doivent-ils extraire ses données à chaque fois de la mémoire GPU, même si un modèle effectue plusieurs rendus séquentiels?

Tout comme les CPU, les GPU sont autorisés à réorganiser les instructions GPU et les opérations d'accès à la mémoire , (lire: exécution hors service ), donc le GPU gérera très probablement le scénario que vous avez mentionné en accédant à la mémoire qui se trouve dans son cache (généralement consulté récemment ), mais parfois il ne peut pas faire cela.

De toute évidence, les cartes graphiques ont une mémoire RAM limitée - quand elles ne peuvent pas contenir toutes les données de modèle nécessaires pour le rendu d'une image, je suppose qu'elle continue de les récupérer (une partie) de la RAM du processeur à chaque image, est-ce correct?

Vous ne voulez pas que cela se produise. Mais peu importe si cela se produit, le GPU commencera à déplacer la mémoire entre RAM et VRAM (le processeur de commande sur le GPU est responsable de cela), ce qui rendra le rendu beaucoup plus lent, ce qui entraînera le blocage du GPU, car il devra attendre les données à copier de / vers V / RAM.

Il y a l'envoi des données au GPU et il y a la définition / liaison des tampons comme actuels. Ce dernier provoque-t-il un flux de données?

Les GPU contiennent un tampon de commande , et toutes les commandes API sont soumises à ce tampon, notez que cela peut se produire simultanément avec les données copiées sur le GPU. Le tampon d'anneau de commande est une file d'attente de communication entre le CPU et le GPU , toute commande qui doit être exécutée doit être soumise à la file d'attente afin qu'elle puisse être exécutée par le GPU. Tout comme toute opération liant de nouveaux tampons doit être soumise au processeur graphique afin qu'il puisse accéder à un emplacement de mémoire.

C'est l'une des raisons pour lesquelles glBegin / glEnd a été déconseillé, soumettre de nouvelles commandes nécessite une synchronisation de la file d'attente (en utilisant des barrières / barrières de mémoire).

entrez la description de l'image ici

Quant à vos autres points:

Création de tampon avec initialisation

Vous pouvez allouer un tampon sans initialisation et le conserver pour une utilisation ultérieure. Ou vous pouvez lui allouer un tampon et copier des données en même temps (en parlant du niveau API).

mise à jour des données du tampon

Vous pouvez utiliser glMapBuffer pour mettre à jour la mémoire côté GPU. Le fait que la mémoire soit copiée de / vers la RAM n'est pas en fait un problème de la norme et variera considérablement en fonction du fournisseur, du type de GPU et du pilote.

Appel de l'API (ici, j'aimerais avoir de vos nouvelles ce qui se passe réellement là-bas).

Mon deuxième point dans la question principale couvre cela.

lier le tampon comme actif (est-ce juste un moyen de dire à l'API que je veux que ce tampon soit rendu dans le prochain appel de dessin et qu'il ne fasse rien par lui-même?)

Considérez la liaison comme l'utilisation d'un thispointeur dans n'importe quel langage orienté objet, bien que n'étant pas strictement le même, tous les appels d'API conséquents seront relatifs à ce tampon de liaison.

concept3d
la source
3

En général, la frontière et l'implication du cpu par rapport au gpu sont spécifiques à la plate-forme, mais la plupart d'entre eux suivent ce modèle: le cpu a du ram, le gpu aussi et vous pouvez déplacer la mémoire (dans certains cas, le ram est partagé mais pour le dans un souci de simplicité, collons-nous à des béliers séparés).

premier point : les données que vous initialisez, vous pouvez choisir de les conserver dans le ram CPU ou sur le ram GPU, et ce sont des avantages pour les deux. Lorsque vous rendez quelque chose, le GPU doit faire le gros du travail, il est donc évident que les données qui sont déjà sur le mem GPU fourniront de meilleures performances. pour le CPU, il doit d'abord envoyer les données au GPU (qui peut choisir de les conserver pendant un certain temps), puis effectuer le rendu.

deuxième point : Il existe de nombreuses astuces dans le rendu, mais la principale chose à faire est avec les polygones. Sur un cadre, le GPU rendra les objets constitués de polygones un par un et après avoir terminé, le GPU enverra l'image à l'écran. Il n'y a pas de concept comme les objets, il n'y a que des polygones et la façon dont vous les assemblerez créera une image. le travail du GPU est de projeter ces polygones de 3d à 2d et d'appliquer un effet (si vous le souhaitez). Les polygones ne vont que de façon simple CPU-> GPU-> SCREEN ou GPU-> SCREEN directement (si les polygones sont déjà dans le ram GPU)

troisième point : lorsque vous effectuez un rendu d'animation, par exemple, il est préférable de garder les données à proximité du processeur car là, il fait le gros du travail, il ne serait pas optimal de conserver les données en GPU, de les déplacer vers le CPU et de reculer chaque image. Il existe de nombreux autres exemples comme celui-ci à compter, mais en général toutes les données resteront proches de celui qui effectue les calculs. Habituellement, vous souhaiterez déplacer autant de données que possible vers le ram GPU pour gagner en performances.

L' envoi réel des données au GPU est effectué par l'API que vous utilisez (directx / opengl ou autre) et le concept de liaison et d'autres choses de ce genre ne sont que des abstractions pour que l'API comprenne ce que vous voulez faire.

Modifier pour l'édition:

  • buffer creation with initialisation: c'est comme la différence entre int a = new int[10]et a[0] = 0,a[1] = 1.... etc lorsque vous créez un tampon, vous faites de la place pour les données et lorsque vous lancez les données, vous y mettez ce que vous voulez.

  • buffer data updatesi c'est sur le ram cpu alors vous avez vertex * verticesque vous pouvez jouer avec, s'il n'est pas là, vous devrez le déplacer du GPU vertex * vertices = map(buffer_id);(la carte est une fonction mythologique qui devrait déplacer les données du GPU vers le ram du CPU, elle a aussi son oposite buffer_id = create_buffer(vertices);

  • binding the buffer as activec'est juste un concept qu'ils appellent le bindingrendu est un processus complexe et c'est comme appeler une fonction avec 10000 paramètres. La liaison n'est qu'un terme utilisé pour dire quel tampon va où. Il n'y a pas vraiment de magie derrière ce terme, il ne convertit pas, ne déplace pas ou ne réalloue pas les tampons, indique simplement au pilote que lors du prochain appel de tirage, utilisez ce tampon.

  • API draw callAprès tous les tampons de fixation et de mise en place, c'est l'endroit où le caoutchouc rencontre la route. L'appel de tirage prendra toutes les données (ou les identifiants qui pointent vers les données) que vous avez spécifiées, les envoyera au GPU (si nécessaire) et dira au GPU de commencer à croquer les chiffres. Ce n'est pas entièrement vrai sur toutes les plates-formes, il existe de nombreuses différences, mais pour garder les choses simples, le tirage indiquera au GPU de .... dessiner.

Raxvan
la source
2

La réponse la plus correcte est, cela dépend de la façon dont vous le programmez, mais c'est une bonne chose à craindre. Alors que les GPU sont devenus incroyablement rapides, la bande passante vers et depuis la RAM du GPU ne l'est pas, et sera votre goulot d'étranglement le plus frustrant.

Ses données sont-elles envoyées à la mémoire du GPU une seule fois et restent-elles là pour toujours?

Esperons que oui. Pour la vitesse de rendu, vous voulez que autant de données soient stockées sur le GPU que possible, au lieu de les renvoyer à chaque image. Les VBO servent exactement ce but. Il existe à la fois des VBO statiques et dynamiques, les premiers étant les meilleurs pour les modèles statiques, et les seconds étant les meilleurs pour les modèles dont les sommets changeront chaque image (par exemple, un système de particules). Même en ce qui concerne les VBO dynamiques, cependant, vous ne voulez pas renvoyer tous les sommets à chaque image; juste ceux qui changent.

Dans le cas de votre bâtiment, les données de sommet resteraient là, et la seule chose qui changerait serait vos matrices (modèle / monde, projection et vue).

Dans le cas d'un système de particules, j'ai créé un VBO dynamique suffisamment grand pour stocker le nombre maximal de particules qui existera jamais pour ce système. Chaque image que j'envoie les données pour les particules émises cette image, avec quelques uniformes, et c'est tout. Lorsque je dessine, je peux spécifier un point de début et de fin dans ce VBO, donc je n'ai pas à supprimer les données de particules. Je peux juste dire de ne pas les dessiner.

Lorsque le modèle est réellement rendu à chaque image, les processeurs GPU doivent-ils récupérer ses données à chaque fois dans la mémoire GPU? Ce que je veux dire, c'est que si j'avais 2 modèles rendus plusieurs fois chacun, cela aurait-il de l'importance si je rendais d'abord le premier plusieurs fois puis le second plusieurs fois ou si je rendais le premier une seule fois, le second une seule fois et continué à l'entrelacer comme ça?

Le fait d'envoyer plusieurs appels de tirage au lieu d'un seul est une limite beaucoup plus grande. Découvrez le rendu instancié; cela pourrait vous aider beaucoup et rendre la réponse à cette question inutile. J'ai eu quelques problèmes de pilote avec lesquels je n'ai pas encore résolu, mais si vous pouvez le faire fonctionner, le problème est résolu.

De toute évidence, les cartes graphiques ont une mémoire RAM limitée - quand elles ne peuvent pas contenir toutes les données de modèle nécessaires pour le rendu d'une image, je suppose qu'elle continue de les récupérer (une partie) de la RAM du processeur à chaque image, est-ce correct?

Vous ne voulez pas manquer de RAM GPU. Si vous le faites, changez les choses pour que vous ne le fassiez pas. Dans le scénario très hypothétique que vous épuisez, cela va probablement se bloquer d'une manière ou d'une autre, mais je ne l'ai jamais vu arriver, donc honnêtement, je ne sais pas.

J'ai oublié de faire une distinction: il y a l'envoi des données au GPU et il y a la définition / liaison des tampons comme courant. Ce dernier provoque-t-il un flux de données?

Pas de flux de données significatif, non. Il y a un certain coût, mais c'est vrai pour chaque ligne de code que vous écrivez. Découvrir combien cela vous coûte est, encore une fois, à quoi sert le profilage.

création de tampon avec initialisation

La réponse de Raxvan semble bonne, mais elle n'est pas tout à fait exacte. Dans OpenGL, la création du tampon ne réserve aucun espace. Si vous souhaitez réserver de l'espace sans transmettre de données, vous pouvez appeler glBufferData et simplement passer null. (Voir la section des notes ici .)

mise à jour des données du tampon

Je suppose que vous voulez dire glBufferData, ou d'autres fonctions comme ça, non? C'est là que se produit le véritable transfert de données. (Sauf si vous passez nul, comme je viens de le dire dans le dernier paragraphe.)

lier le tampon comme actif (est-ce juste un moyen de dire à l'API que je veux que ce tampon soit rendu lors du prochain appel de dessin et qu'il ne fasse rien par lui-même?)

Oui, mais cela peut faire un peu plus que cela. Par exemple, si vous liez un VAO (objet tableau de vertex), puis liez un VBO, ce VBO devient lié au VAO. Plus tard, si vous liez à nouveau ce VAO et appelez glDrawArrays, il saura quel VBO dessiner.

Notez que bien que de nombreux didacticiels vous permettront de créer un VAO pour chaque VBO, on m'a dit que ce n'était pas leur utilisation prévue. Soi-disant, vous devez créer un VAO et l'utiliser avec chaque VBO qui a les mêmes attributs. Je n'ai pas encore essayé cela, donc je ne peux pas dire avec certitude si c'est mieux ou pire.

Appel d'appel API

Ce qui se passe ici est assez simple (de notre point de vue). Supposons que vous liez un VAO, puis appelez glDrawArrays. Vous spécifiez un point de départ et un nombre, et il exécute votre vertex shader pour chaque sommet de cette plage, qui à son tour transmet ses sorties sur la ligne. Cependant, ce processus est un autre essai en soi.

Défiance glaciale
la source
"alors le problème est résolu" Oui, l'instanciation aiderait beaucoup mais sans cela, je devrais encore faire un appel de dessin pour chaque objet. La même chose dans les deux cas. Je me demande donc si l'ordre est important.
NPS
@NPS - Cela compte pour certains . S'ils sont commandés pour que vous n'ayez pas à changer de fixations, oui, ce sera probablement une quantité infime plus rapide. Mais si vous devez vous démener pour les trier, ce sera probablement beaucoup, beaucoup plus coûteux. Il y a trop de variables dépendantes de votre implémentation pour en dire bien plus.
Icy Defiance