web-dev-qa-db-fra.com

opengl: glFlush () vs glFinish ()

Je ne parviens pas à distinguer la différence pratique entre appeler glFlush() et glFinish().

Les docs disent que glFlush() et glFinish() pousseront toutes les opérations mises en mémoire tampon vers OpenGL afin que l'on puisse être sûr qu'elles seront toutes exécutées, la différence étant que glFlush() retournera Immédiatement où glFinish() bloque jusqu'à ce que toutes les opérations soient terminées.

Après avoir lu les définitions, j’ai pensé que si j’utilisais glFlush(), je rencontrerais probablement le problème de soumettre plus d’opérations à OpenGL qu’elles ne pourraient en exécuter. Donc, juste pour essayer, j'ai échangé mon glFinish() contre un glFlush() et voilà, mon programme a fonctionné (autant que je sache), exactement le même; taux de trame, utilisation des ressources, tout était pareil.

Je me demande donc s'il y a une grande différence entre les deux appels ou si mon code ne les rend pas différents. Ou où l'un devrait être utilisé par rapport à l'autre. J'ai aussi pensé qu'OpenGL aurait un appel comme glIsDone() pour vérifier si toutes les commandes mises en mémoire tampon pour un glFlush() sont complètes ou non (ainsi on n'envoie pas d'opérations à OpenGL plus rapidement qu'ils peuvent être exécutés), mais je ne pouvais pas trouver une telle fonction.

Mon code est la boucle de jeu typique:

while (running) {
    process_stuff();
    render_stuff();
}
101
jay.lee

Notez que ces commandes existent depuis les débuts d'OpenGL. glFlush garantit que les commandes OpenGL précédentes doivent se terminer en temps fini ( spécifications OpenGL 2.1 , page 245). Si vous dessinez directement dans la mémoire tampon avant, cela garantira que les pilotes OpenGL commencent à dessiner sans trop tarder. Vous pouvez penser à une scène complexe qui apparaît objet après objet à l'écran lorsque vous appelez glFlush après chaque objet. Cependant, lors de l'utilisation de la double mise en tampon, glFlush n'a pratiquement aucun effet, car les modifications ne seront visibles que lorsque vous aurez échangé les mises en mémoire tampon.

glFinish ne retourne pas avant que tous les effets des commandes précédemment émises [...] soient pleinement réalisés . Cela signifie que l'exécution de votre programme attend ici jusqu'à ce que chaque dernier pixel soit dessiné et qu'OpenGL n'ait plus rien à faire. Si vous rendez directement dans la mémoire tampon avant, glFinish est l'appel à effectuer avant d'utiliser les appels du système d'exploitation pour prendre des captures d'écran. Il est beaucoup moins utile pour la double mise en tampon, car vous ne voyez pas les modifications que vous avez forcées à effectuer.

Donc, si vous utilisez le double tampon, vous n'aurez probablement besoin ni de glFlush ni de glFinish. SwapBuffers dirige implicitement les appels OpenGL vers le tampon correct, il n'est pas nécessaire d'appeler d'abord glFlush . Et n'hésitez pas à insister sur le pilote OpenGL: glFlush ne s'étouffera pas avec trop de commandes. Il n'est pas garanti que cet appel retourne immédiatement (peu importe ce que cela signifie), le traitement de vos commandes peut donc prendre à tout moment.

90
Malte Clasen

Comme les autres réponses l'ont laissé entendre, il n'y a vraiment pas de bonne réponse selon les spécifications. L'intention générale de glFlush() est qu'après l'appel, la CPU hôte n'aura aucun travail à faire sur OpenGL. Les commandes auront été placées dans le matériel graphique. L'intention générale de glFinish() est qu'après le retour, non le travail restant est laissé, et les résultats doivent être disponibles aussi pour toutes les API non OpenGL appropriées (par exemple, les lectures à partir du framebuffer, captures d'écran, etc ...). Que ce soit vraiment le cas, cela dépend du conducteur. La spécification laisse une grande latitude quant à ce qui est légal.

21
Andy Ross

J'étais toujours confus à propos de ces deux commandes aussi, mais cette image m'a tout expliqué: Flush vs Finish Apparemment, certains pilotes GPU n’envoient pas les commandes émises au matériel sauf s’ils ont accumulé un certain nombre de commandes. Dans cet exemple, ce nombre est 5 .
L'image montre diverses commandes OpenGL (A, B, C, D, E ...) qui ont été émises. Comme nous pouvons le voir en haut, les commandes ne sont pas encore émises, car la file d'attente n'est pas encore pleine.

Au milieu, nous voyons comment glFlush() affecte les commandes en file d'attente. Il indique au pilote d'envoyer toutes les commandes mises en file d'attente au matériel (même si la file d'attente n'est pas encore pleine). Cela ne bloque pas le fil d'appel. Cela indique simplement au pilote que nous n'envoyons peut-être pas de commandes supplémentaires. Par conséquent, attendre que la file soit complète serait une perte de temps.

En bas, nous voyons un exemple utilisant glFinish(). Il fait presque la même chose que glFlush(), sauf qu'il fait attendre le thread appelant jusqu'à ce que toutes les commandes aient été traitées par le matériel.

Image tirée du livre "Programmation graphique avancée avec OpenGL".

14
Tara

Si vous ne voyez aucune différence de performances, cela signifie que vous faites quelque chose de mal. Comme d'autres l'ont déjà mentionné, vous n'avez pas besoin d'appeler non plus, mais si vous appelez glFinish, vous perdez automatiquement le parallélisme que le processeur graphique et le processeur peuvent réaliser. Laissez-moi plonger plus profondément:

En pratique, tout le travail que vous soumettez au pilote est mis en lot et envoyé potentiellement au matériel ultérieurement (par exemple, au moment de SwapBuffer).

Donc, si vous appelez glFinish, vous forcez essentiellement le pilote à pousser les commandes vers le GPU (jusqu'à ce jour, et n'a jamais demandé au GPU de fonctionner) et à bloquer le processeur jusqu'à ce que les commandes soient complètement remplies. réalisé. Donc, pendant tout le temps que le GPU fonctionne, le processeur ne le fait pas (du moins sur ce thread). Et tout le temps que le processeur fait son travail (principalement des commandes de traitement en lots), le GPU ne fait rien. Alors oui, glFinish devrait nuire à votre performance. (Ceci est une approximation, car les pilotes peuvent avoir à faire en sorte que le GPU fonctionne sur certaines commandes si beaucoup d'entre elles ont déjà été mises en lot. Ce n'est pas typique, car les tampons de commandes ont tendance à être assez grands pour contenir beaucoup de commandes).

Maintenant, pourquoi voudriez-vous appeler glFinish? Les seules fois où je l'ai utilisé étaient quand j'avais des bugs de pilotes. En effet, si l'une des commandes que vous envoyez au matériel plante le processeur graphique, l'option la plus simple pour identifier la commande en cause est d'appeler glFinish après chaque tirage. De cette façon, vous pouvez limiter ce qui déclenche le crash.

De plus, les API telles que Direct3D ne prennent pas en charge le concept de finition.

6
Bahbar

glFlush remonte vraiment à un modèle de serveur client. Vous envoyez toutes les commandes gl par un canal à un serveur gl. Ce tuyau pourrait tamponner. Tout comme n'importe quel fichier ou réseau, une entrée/sortie peut être mise en mémoire tampon. glFlush dit seulement "envoie le tampon maintenant, même s'il n'est pas encore plein!". Sur un système local, cela n'est presque jamais nécessaire, car une API OpenGL locale a peu de chances de se mettre en tampon elle-même et émet simplement des commandes directement. De plus, toutes les commandes qui entraînent un rendu réel effectueront un vidage implicite.

glFinish, par contre, a été conçu pour mesurer les performances. Type de PING sur le serveur GL. Il effectue un aller-retour vers une commande et attend que le serveur réponde "Je suis inactif".

De nos jours, les chauffeurs locaux ont des idées assez créatives pour comprendre ce que signifie être inactif. S'agit-il de "tous les pixels sont dessinés" ou de "ma file d'attente de commandes contient de l'espace"? De plus, parce que de nombreux programmes anciens ont aspergé glFlush et glFinish dans leur code sans raison, comme le codage voodoo, de nombreux pilotes modernes les ignorent simplement comme une "optimisation". Je ne peux pas leur en vouloir, vraiment.

Donc, en résumé: Traitez à la fois glFinish et glFlush comme des opérations non pratiques, sauf si vous codez pour un ancien serveur SGI OpenGL distant.

5
starmole

Regardez ici . En bref, il est écrit:

glFinish () a le même effet que glFlush (), avec en plus que glFinish () bloquera jusqu'à ce que toutes les commandes soumises aient été exécutées.

Un autre article décrit d'autres différences:

  • Les fonctions de swap (utilisées dans les applications à double tampon) effacent automatiquement les commandes, il n'est donc pas nécessaire d'appeler glFlush
  • glFinish force OpenGL à exécuter des commandes exceptionnelles, ce qui est une mauvaise idée (par exemple, avec VSync)

En résumé, cela signifie que vous n’avez même pas besoin de ces fonctions lorsque vous utilisez la double mise en mémoire tampon, sauf si l’implémentation de vos mémoires tampons d'échange ne vide pas automatiquement les commandes.

4
AndiDog

Il ne semble pas y avoir de moyen d'interroger le statut du tampon. Il y a ceci extension Apple qui pourrait servir au même but, mais cela ne semble pas multiplate-forme (je ne l'ai pas essayé.) À première vue, il semble qu'il soit antérieur à flush vous enfonceriez la commande fence; vous pouvez alors interroger le statut de cette clôture lorsqu'elle se déplace dans le tampon.

Je me demande si vous pourriez utiliser flush avant de mettre en mémoire tampon les commandes, mais avant de commencer à rendre la trame suivante que vous appelez finish. Cela vous permettrait de commencer à traiter la prochaine image lorsque le GPU fonctionne, mais si cela n’est pas fait au moment de votre retour, finish se bloque pour s’assurer que tout est à l’état frais.

Je n'ai pas essayé cela, mais je le ferai bientôt.

Je l'ai essayé sur une ancienne application qui utilise même le processeur et le processeur graphique. (À l'origine, il utilisait finish.)

Lorsque je l'ai remplacé par flush à la fin et par finish au début, il n'y a pas eu de problèmes immédiats. (Tout semblait aller bien!) La réactivité du programme a augmenté, probablement parce que le processeur n'était pas bloqué dans l'attente du GPU. Certainement une meilleure méthode.

À des fins de comparaison, j'ai supprimé finished du début de la trame, en laissant flush, et le résultat a été identique.

Je dirais donc que vous utilisez flush et finish, car lorsque le tampon est vide lors de l'appel de finish, il n'y a pas d'impact sur les performances. Et je suppose que si le tampon était plein, vous devriez quand même vouloir finish.

3
GManNickG

La question est de savoir si vous souhaitez que votre code continue à s'exécuter pendant l'exécution des commandes OpenGL ou seulement à s'exécuter après vos commandes OpenGL ont été exécutées.

Cela peut être important dans certains cas, comme les retards sur le réseau, de n’avoir que certaines sorties de console après les images ont été dessinées ou autres.

0
anon