C'est pas tous les jours que j'écris dans cette section, mais j'ai un besoin pas commun.
Voilà j'avais dans l'idée récemment d'utiliser des fonctions OpenGL dans mes programmes, pas pour faire du rendu à proprement parler, mais pour utiliser les fonctionnalités de dessin de polygones. Enfin bref. Après quelques recherches, j'ai vu que le rendu "offline" était parfaitement possible en utilisant des buffers spécifiques. En revanche j'anticipe des soucis de performances au niveau de la lecture des images générées, la fonction glReadPixels étant (d'après la doc et les tests lus en ligne) particulièrement lente.
D'où plusieurs questions :
- y a-t-il des fonctions plus adaptées pour transférer le contenu d'un buffer vers la mémoire principale de mon PC ? (mettons par exemple que je veuille appliquer un traitement software sur mon image générée par le hard). J'aurais pensé que c'était envisageable et que c'était même utilisé dans certains jeux, mais quand je vois des temps de glReadPixels de l'ordre de 10ms, je me demande comment ils peuvent utiliser ça en temps réel...
- pourrais-je éventuellement utiliser OpenGL dans un mode plus approprié à ce que je veux faire ? Je dis peut-être n'importe quoi, mais ne peut-on pas le faire tourner en mode "software", auquel cas je perdrais les performances du proc graphique, mais j'y gagnerais peut-être en transfert de données ?
- le goulot d'étranglement étant la bande passante entre les mémoires, ce problème peut-il être résolu :
* en utilisant un système à mémoire partagée ? (je suis un gros newb sur la question mais en théorie si la mémoire est partagée, c'est plus une question de droits d'accès que de transfert, non ?)
* en utilisant une config qui a une bande passante qui dépote, genre une PS3 ? il me semblait d'ailleurs que c'était une des spécificités intéressantes de la bécane, que le calculateur central et le GPU pouvaient s'échanger des données très rapidement pour se partager le travail (d'où question subsidiaire : si je colle mon programme OpenGL bête et méchant sur une PS3, est-ce que je peux profiter de ses performances dans ce domaine ?)
J'ai aussi des questions rigolotes sur l'utilisation de vertex shaders pour simuler de la distorsion de type "fisheye", mais je les garde pour plus tard...
Bon je ne sais pas si ça va vraiment t'aider mais je peux déjà te dire ceci :
Tout d'abord le problème n'est pas vraiment au niveau de la Bande Passante (en PCI Express 16x tu as 4Go/s de BP descendante théorique -beaucoup moins en pratique - donc de quoi ramener plus de 500 frames full HD par seconde) là c'est vraiment une question de latence : lorsque tu envoies des informations au GPU par le biais de ton API elles ne sont pas traitées immédiatement, elles passent d'abord dans un tampon dans lequel le GPU vient piocher au fur et à mesure, ça permet de maximiser le travail en parallèle entre le CPU et le GPU mais en contrepartie ça accroit la latence des opérations. le GPU lui même est une machine conçue pour tolérer les grandes latences, c'est ce qui lui permet d'être efficace : écrire un pixel dans le framebuffer peut prendre 600 ou 700 cycles mais ça ne fait rien car il traite des centaines de pixels en parallèle et comme au final l'image en comporte des milliers ça ne pose pas de problème. Bref un GPU à un gros débit (beaucoup d'éléments traités par seconde) mais en contrepartie une grosse latence (beaucoup de cycles passés entre le début d'une opération et la fin) ce qui est très adapté au rendu.
Lorsque tu fais un readpixels tu brises le parallélisme CPU/GPU : tu forces le GPU à finir toutes les opérations dans le tampon, et pendant ce temps le CPU ne peut pas écrire de nouvelles commandes dans le tampon, readpixels est une opération synchrone par défaut donc elle bloque le CPU et le GPU pendant le transfert.
le problème c'est qu'il n'y a guère que glReadPixels pour rapatrier le framebuffer dans la RAM principale du PC, par contre une nouvelle extension est disponible pour pouvoir faire des lectures asynchrones : les pixel buffer objects. La lecture devient ainsi non bloquante (sur le CPU) du coup même si la latence ne bougera pas, il faudra toujours 10ms pour récupérer l'image, mais au lieu de perdre 10ms de temps CPU qui se tourne les pouces, il peut faire un travail utile en effectuant ton traitement sur la frame n-1, c'est le principe du pipeline : il y a un recouvrement entre les différentes étapes de ton opération.
Pour la PS3 le problème est que tu n'as pas accès au GPU avec le kit Linux, après il y a peut être une implémentation software (MESA) qui a été porté sur le Cell mais là je n'en sais vraiment pas plus et je ne sais pas si ce serait intéressant pour ce que tu veux faire, et même si tu pouvais utiliser le RSX tu gagneras en BP (ça doit être 15Go/s la BP descendante du FlexIO au lieu de 4 en PCI Express, à vérifier) mais pas en latence donc là encore ça n'est pas vraiment idéal.
Autre petit détail rigolo : les perfs de glReadPixels dépendent aussi du format de l'image car les données en mémoire vidéo sont en général tilé et swizzlé (désolé j'ai pas de meilleur terme ) pour optimiser les performances d'écriture du GPU, du coup quand tu les recopies en RAM le driver en général doit les remettre dans le bon ordre ce qui a un impact différent en fonction de ce que tu veux comme format.
Enfin voilà tu vas bien t'amuser à tenter de maximiser tes performances la clé va être d'équilibrer la charge CPU/GPU, de bufferiser juste ce qu'il te faut pour que tu perdes le moins de temps possible, ça va être un sacré casse tête mais c'est aussi le charme de la programmation non
En tout cas bon courage, et même si j'ai pas vraiment répondu à tes questions j'espère t'avoir donné quelques pistes utiles
Pas grand-chose à ajouter à l'excellent résumé de Zeross (mais comme j'avais commencé à préparer ma réponse tout à l'heure, je poste quand même, na )...
Le truc, c'est que si tu envoies à la carte vidéo des dizaines de commandes, et que tu demandes le résultat, tu attends le temps qu'il lui faut pour exécuter les commandes en questions. Heureusement, le processeur n'attends pas qu'elles soient exécutées avant de passer à la suite. Donc si tu veux faire ce genre de choses, la latence de glReadPixels se cache très bien avec n'importe quelle autre action que tu aurais à faire : préparation de la frame suivante comme le suggère Zeross (ce qui inclut physique, IA...), interaction avec l'utilisateur, précalculs (pour accélérer le traitement du buffer récupéré, il m'est arrivé de mettre à profit la latence pour créer une LUT qui a permi de diviser par trois le traitement du buffer ensuite, ce qui était tout bénef, puisque je n'avais rien d'autre à faire en attendant, et la LUT a largement regagné les quelques ms que je devais attendre), etc. Et à partir de là, ça fonctionne.
Par exemple, ça ressemble à ça : - le CPU exécute trois commandes OpenGL en rouge/vert/bleu - il exécute un peu de code non lié à la carte en gris - il fait le readpixel - il est bloqué jusqu'à ce que la carte ait fini de traiter les commandes OpenGL et ait pu envoyer les données (ce qui se fait relativement vite)
Ergo toute l'astuce consiste à mettre tout ce que l'on peut dans la partie grise pour éviter tout blocage du CPU. Tu dois pouvoir d'ailleurs forker ton programme avant, faire le readpixels d'un côté, lancer un traitement de l'autre. Le premier thread va bloquer, le second faire du traitement. Quand le glReadPixel retourne, tu peux couper le thread qui avait pour but d'occuper le processeur prendant ce temps si besoin (je ne suis pas complètement sûr que ça fonctionne, je n'ai jamais eu besoin de ça dans la mesure où j'ai toujours pu masquer la latence avec autre chose et je n'avais pas besoin d'avoir le retour de glReadPixels aussitôt que possible. Sans doute que Zeross pourra confirmer).
- pourrais-je éventuellement utiliser OpenGL dans un mode plus approprié à ce que je veux faire ? Je dis peut-être n'importe quoi, mais ne peut-on pas le faire tourner en mode "software", auquel cas je perdrais les performances du proc graphique, mais j'y gagnerais peut-être en transfert de données ?
Ca dépend de ce que tu as à faire, mais je doute que tu arrives à faire des rendus très compliqués en un temps très inférieur à 10ms (histoire de ne pas perdre ailleurs ce que tu gagnes). Je suppose que tu n'as aucune façon de transférer ton traitement côté GPU ? Il y a pas mal de trucs et astuces pour faire diverses choses (d'ailleurs, si tu peux en dire un peu plus sur ce que tu veux faire, ça aidera sans doute).
J'ai aussi des questions rigolotes sur l'utilisation de vertex shaders pour simuler de la distorsion de type "fisheye", mais je les garde pour plus tard...
Il y a des solutions sans shaders, aussi, si tu veux (à coup de render to texture), ça permet de ne pas trop se prendre la tête dans certaines situations, mais évidemment l'efficacité est un peu moindre.
Merci pour toutes ces infos. En farfouillant un peu, je suis aussi tombé sur Mesa, ça pourrait être une piste intéressante pour ce que je veux faire, c'est à explorer.
En fait je ne cherche pas à générer des scènes complexes, l'idée est de générer plein de petites scènes élémentaires (quelques milliers de polys max, pas de textures, pas d'éclairage fantaisiste), de façon aléatoire ou itérative, avec un calcul de coût pour chaque image générée (par exemple "nombre de pixels visibles" ), afin de converger vers une scène "optimale".
Dans un premier temps je comptais faire les calculs de coût dans la RAM principale, mais avec de telles latences c'est pas la peine (ou alors faut voir en mode software). J'imagine que ça doit être faisable au niveau du GPU mais je n'ai pas encore bien réfléchi à la chose.
Dans tous les cas il faudra quand même que je remonte régulièrement et rapidement des infos vers le processeur central, pour qu'il puisse relancer une nouvelle fournée de traitements ou simplement utiliser les résultats retournés par le GPU et procéder à la suite du programme...
Enfin plus j'y pense et plus je me demande si c'est vraiment faisable tout ça. Ce dont j'ai besoin en fait c'est essentiellement une fonctionnalité d'affichage performant de polygones dans des images, mais je sens que je vais perdre en communication avec le GPU tout ce que je vais gagner en vitesse de tracé. Ou alors faudrait que je "pipeline-ise" tout ça, comme il se doit, mais je sens que je vais m'arracher les cheveux...
Dans un premier temps je comptais faire les calculs de coût dans la RAM principale, mais avec de telles latences c'est pas la peine (ou alors faut voir en mode software).
A mon avis, ce sera plus lent en soft. J'ai rarement vu Mesa aller plus vite qu'une carte, même ancienne, et même pour une scène simple. Ca ne paraît pas non plus simple à mettre dans le GPU s'il faut qu'un rendu décide d'un autre rendu...
Enfin plus j'y pense et plus je me demande si c'est vraiment faisable tout ça. Ce dont j'ai besoin en fait c'est essentiellement une fonctionnalité d'affichage performant de polygones dans des images, mais je sens que je vais perdre en communication avec le GPU tout ce que je vais gagner en vitesse de tracé. Ou alors faudrait que je "pipeline-ise" tout ça, comme il se doit, mais je sens que je vais m'arracher les cheveux...
Pas nécessairement... Après tout, la latence, c'est essentiellement le temps que le GPU trace l'image. Si elle est très simple, tu n'auras pas de latence notable. Et de toute façon, si tu as N rendu à faire, il faudra bien les faire... la seule chose que tu vas devoir attendre, c'est que les rendus soient faits.
Tu peux accélérer les choses en rusant : si tu veux juste une silhouette (j'ai fait du tracking comme ça, en optimisant le recouvrement du modèle OpenGL et la silhouette extraite d'une vidéo, et ça marche via readpixels), tu n'as pas besoin de Zbuffer, pas besoin de couleur (possibilité de prendre un buffer avec une profondeur de couleur faible), pas besoin d'antialiasing, etc. Tout ce que tu enlèves, c'est ça de pris...
A noter un truc : si tu veux compter le nombre de pixels blancs sur un fond noir, tu peux faire le rendu dans une texture, activer le mipmapping, afficher la texture dans le front buffer à une résolution 16 fois moindre, et récupérer l'image réduite (256 fois plus petite). Tu auras une image bien plus petite à transférer, et la valeur de chaque pixel correspondra au nombre de pixels blancs dans un carré de 16x16. C'est toujours ça de moins à calculer
A mon avis, ce sera plus lent en soft. J'ai rarement vu Mesa aller plus vite qu'une carte, même ancienne, et même pour une scène simple. Ca ne paraît pas non plus simple à mettre dans le GPU s'il faut qu'un rendu décide d'un autre rendu...
Pour ça je pense qu'il y a moyen de ruser, ne pas faire des rendus successifs les uns après les autres mais par paquets de rendus indépendants entre eux, et c'est le résultat de chaque paquet qui déterminera le paquet suivant... un peu comme du Monte-Carlo, quoi.
Pas nécessairement... Après tout, la latence, c'est essentiellement le temps que le GPU trace l'image. Si elle est très simple, tu n'auras pas de latence notable. Et de toute façon, si tu as N rendu à faire, il faudra bien les faire... la seule chose que tu vas devoir attendre, c'est que les rendus soient faits.
Ha ok, dans mon esprit la latence venait du transfert de données à proprement parler, si ce n'est qu'une question de traitement alors ça doit être gérable, puisque ce que je vais dessiner est vraiment minimaliste. D'ailleurs ce que j'ai en tête doit ressembler à ce que tu as fait en suivi de silhouette dans une vidéo, sur le principe (tu obtenais quoi comme perfs d'ailleurs, si ce n'est pas indiscret ?)
Merci au fait pour l'astuce du comptage de pixels, je pense que ça va servir. Va falloir que je me penche pas mal sur les différentes opérations qu'on peut réaliser sur une CG, jusqu'à présent je n'ai fait que du rendu de base en OpenGL, pas vraiment de calculs logiques ou mathématiques, et il va m'en falloir un minimum.
Ha ok, dans mon esprit la latence venait du transfert de données à proprement parler, si ce n'est qu'une question de traitement alors ça doit être gérable, puisque ce que je vais dessiner est vraiment minimaliste. D'ailleurs ce que j'ai en tête doit ressembler à ce que tu as fait en suivi de silhouette dans une vidéo, sur le principe (tu obtenais quoi comme perfs d'ailleurs, si ce n'est pas indiscret ?)
Va falloir déterminer le sens "perfs"... Je crois que ça devait faire plusieurs milliers de rendus à la seconde (pas en très grosse résolution) en rusant. Pendant que le GPU calculait une série de poses de l'objet (dans un seul buffer, mais divisé en N cases en jouant sur les caméras, les zones d'écriture, etc), je préparais une display list pour la série de poses suivantes. Du coup, je ne souffrais pas de la latence... Ca permettait d'avoir des résultats corrects, mais le problème, c'est que j'avais trop de degrés de liberté, donc ça n'a jamais été terriblement efficace, et j'ai cherché dans une autre voie.
Va falloir que je me penche pas mal sur les différentes opérations qu'on peut réaliser sur une CG, jusqu'à présent je n'ai fait que du rendu de base en OpenGL, pas vraiment de calculs logiques ou mathématiques, et il va m'en falloir un minimum.
Il n'y a pas forcément beaucoup de fonctions disponibles, tout est dans la ruse (l'essentiel des fonctions "logiques et mathématiques" sont des détournements)... Tu bidouilles des moyennes par mipmapping, des fonctions mathématiques en te servant de textures comme de tableaux de valeurs, des multiplications en utilisant plusieurs passes, etc.