opengl: glFlush () contre glFinish ()

105

J'ai du mal à distinguer la différence pratique entre appeler glFlush()et glFinish().

La documentation dit cela glFlush()et glFinish()poussera toutes les opérations tamponnées vers OpenGL afin que l'on puisse être assuré qu'elles seront toutes exécutées, la différence étant que glFlush()retourne immédiatement où sous forme de glFinish()blocs jusqu'à ce que toutes les opérations soient terminées.

Après avoir lu les définitions, j'ai pensé que si je devais utiliser glFlush()cela, je me heurterais probablement au problème de soumettre plus d'opérations à OpenGL qu'il ne pourrait en exécuter. Donc, juste pour essayer, j'ai troqué mon glFinish()pour un glFlush()et voilà, mon programme a fonctionné (pour autant que je sache), exactement la même chose; taux de trame, utilisation des ressources, tout était pareil.

Je me demande donc s'il y a beaucoup de différence entre les deux appels, ou si mon code les fait fonctionner pas différemment. Ou où l'un devrait être utilisé par rapport à l'autre. J'ai également pensé qu'OpenGL aurait un appel comme glIsDone()pour vérifier si toutes les commandes tamponnées pour un glFlush()sont complètes ou non (donc on n'envoie pas d'opérations à OpenGL plus rapidement qu'elles ne peuvent être exécutées), mais je n'ai pas pu trouver une telle fonction .

Mon code est la boucle de jeu typique:

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

Réponses:

93

Notez que ces commandes existent depuis les premiers jours d'OpenGL. glFlush garantit que les commandes OpenGL précédentes doivent être exécutées en un temps fini ( Spécifications OpenGL 2.1 , page 254). Si vous dessinez directement dans le tampon frontal, cela garantira que les pilotes OpenGL commencent à dessiner sans trop de retard. Vous pourriez 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 du double tampon, glFlush n'a pratiquement aucun effet, car les modifications ne seront pas visibles tant que vous n'aurez pas échangé les tampons.

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

Donc, si vous utilisez un double tampon, vous n'aurez probablement besoin ni de glFlush ni de glFinish. SwapBuffers dirige implicitement les appels OpenGL vers le bon tampon, il n'est pas nécessaire d'appeler d'abord glFlush . Et ne vous inquiétez pas de souligner le pilote OpenGL: glFlush ne s'étouffera pas avec trop de commandes. Il n'est pas garanti que cet appel retourne immédiatement (quoi que cela signifie), donc cela peut prendre tout le temps nécessaire pour traiter vos commandes.

Malte Clasen
la source
1
Que voulez-vous dire que glFlush "doit se terminer en un temps FINI" et ensuite dire que glFlush ne s'étouffera pas parce que "cela peut prendre TOUT le temps nécessaire pour traiter vos commandes"? (Caps mine) Ne sont-ils pas mutuellement exclusifs?
Praxitèle
1
Tant qu'il se termine à un moment donné, il répond aux critères de temps FINIS. Certains conducteurs ne font pas cet appel entièrement.
chrisvarnz
SwapBuffers est spécifique au contexte et n'a besoin que de vider le contexte du thread appelant. Si vous effectuez le rendu de plusieurs contextes, chacun doit être vidé manuellement car il n'est pas garanti que cela se produise lors de l'échange de tampons via un autre contexte.
Andreas
22

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, le CPU hôte n'aura aucun travail lié à OpenGL à faire - les commandes auront été poussées vers le matériel graphique. L'intention générale de glFinish()est qu'après son retour, il ne reste plus de travail, et les résultats doivent être disponibles également pour toutes les API non OpenGL appropriées (par exemple, les lectures du framebuffer, les captures d'écran, etc.). Que ce soit vraiment ce qui se passe dépend du conducteur. La spécification permet une tonne de latitude quant à ce qui est légal.

Andy Ross
la source
18

J'ai toujours été confus au sujet de ces deux commandes aussi, mais cette image m'a tout clarifié: Flush vs Finish apparemment, certains pilotes GPU n'envoient pas les commandes émises au matériel à moins qu'un certain nombre de commandes n'ait été accumulé. 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 en file d'attente au matériel (même si la file d'attente n'est pas encore pleine). Cela ne bloque pas le thread appelant. Il signale simplement au pilote que nous n'enverrons peut-être pas de commandes supplémentaires. Par conséquent, attendre que la file d'attente se remplisse 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 "Advanced Graphics Programming Using OpenGL".

Tara
la source
En fait, je sais quoi glFlushet glFinishfaire, et je ne peux pas dire ce que dit cette image. Qu'y a-t-il du côté gauche et du côté droit? De plus, cette image a-t-elle été publiée dans le domaine public ou sous une licence qui vous permet de la publier sur Internet?
Nicol Bolas
J'aurais dû élaborer un peu. À propos de la licence: je ne suis pas tout à fait sûr. Je suis tombé sur le PDF sur Google, tout en faisant des recherches.
Tara du
7

Si vous ne voyez aucune différence de performance, cela signifie que vous faites quelque chose de mal. Comme certains l'ont mentionné, vous n'avez pas besoin d'appeler non plus, mais si vous appelez glFinish, vous perdez automatiquement le parallélisme que le GPU et le CPU peuvent atteindre. Laissez-moi aller plus loin:

En pratique, tout le travail que vous soumettez au pilote est groupé, et envoyé au matériel éventuellement plus tard (par exemple au moment de SwapBuffer).

Donc, si vous appelez glFinish, vous forcez essentiellement le pilote à pousser les commandes vers le GPU (qu'il a groupé jusque-là, et n'a jamais demandé au GPU de travailler), et bloquez le CPU jusqu'à ce que les commandes poussées soient complètement réalisé. Ainsi, pendant tout le temps que le GPU fonctionne, le CPU ne fonctionne pas (du moins sur ce thread). Et tout le temps que le CPU fait son travail (principalement des commandes de traitement par lots), le GPU ne fait rien. Alors oui, glFinish devrait nuire à vos performances. (Ceci est une approximation, car les pilotes peuvent commencer à faire fonctionner le GPU sur certaines commandes si beaucoup d'entre elles étaient déjà groupées. Ce n'est pas typique cependant, car les tampons de commandes ont tendance à être assez grands pour contenir un grand nombre de commandes).

Maintenant, pourquoi appelleriez-vous glFinish alors? Les seules fois où je l'ai utilisé, c'est lorsque j'ai eu des bogues de pilote. En effet, si l'une des commandes que vous envoyez au matériel plante le GPU, alors l'option la plus simple pour identifier la commande coupable est d'appeler glFinish après chaque Draw. De cette façon, vous pouvez préciser ce qui déclenche exactement le crash

En remarque, les API comme Direct3D ne prennent pas du tout en charge un concept Finish.

Bahbar
la source
5
Sur "Maintenant, pourquoi appelleriez-vous glFinish alors?". Si vous chargez des ressources (par exemple: des textures) sur un thread et que vous les utilisez pour rendre sur un autre, avec le partage via wglShareLists ou similaires, vous devez appeler glFinish avant de signaler au thread de rendu qu'il est libre de rendre ce qui a été chargé de manière asynchrone.
Marco Mp
Comme je l'ai dit ci-dessous, cela ressemble à une solution de contournement du bogue du pilote. Je ne trouve aucune mention de spécification de cette exigence. Sous Windows, le pilote doit verrouiller, sur Mac, vous devez faire CGLLockContext. Mais utiliser glFinish semble très faux. Pour savoir quand une texture est valide, vous devez dans tous les cas faire votre propre verrouillage ou événement.
starmole
1
opencl opengl interop docs recommande glFinish pour la synchronisation "Avant d'appeler clEnqueueAcquireGLObjects, l'application doit s'assurer que toutes les opérations GL en attente qui accèdent aux objets spécifiés dans mem_objects sont terminées. Cela peut être accompli de manière portable en émettant et en attendant l'achèvement d'une commande glFinish sur tous Contextes GL avec références en attente à ces objets "
Mark Essel
1
vous n'avez pas "besoin" d'appeler glFinish, vous pouvez utiliser des objets de synchronisation.
chrisvarnz
Juste pour ajouter, depuis la réponse originale: certaines implémentations GL ont commencé à utiliser flush / finish pour synchroniser entre des contextes partagés sur différents threads: developer.apple.com/library/ios/documentation/3DDrawing/... - Plus particulièrement Apple IOS. Je pense que c'est fondamentalement une autre mauvaise utilisation de l'API. Mais ceci est une mise en garde à ma réponse initiale: sur certaines implémentations, la finition / le rinçage sont nécessaires. Mais l'implémentation définit le comment et le pourquoi, pas la spécification OpenGL.
starmole
7

glFlush remonte vraiment à un modèle client-serveur. Vous envoyez toutes les commandes gl via un tube vers un serveur gl. Ce tuyau pourrait tamponner. Tout comme n'importe quel fichier ou entrée / sortie réseau peut mettre en mémoire tampon. glFlush dit seulement "envoyer 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 est peu susceptible de se mettre en mémoire tampon et émet simplement des commandes directement. De plus, toutes les commandes qui provoquent un rendu réel effectueront un vidage implicite.

glFinish, quant à lui, a été conçu pour mesurer les performances. Une sorte de PING vers le serveur GL. Il effectue un aller-retour d'une commande et attend que le serveur réponde "Je suis inactif".

De nos jours, les conducteurs locaux modernes ont des idées assez créatives sur ce que signifie être inactif. Est-ce "tous les pixels sont dessinés" ou "ma file de commandes a de l'espace"? Aussi parce que de nombreux anciens programmes ont saupoudré glFlush et glFinish dans tout leur code sans raison en tant que codage vaudou, de nombreux pilotes modernes les ignorent simplement comme une "optimisation". Je ne peux pas les blâmer pour ça, vraiment.

Donc, en résumé: considérez à la fois glFinish et glFlush comme aucune opération en pratique, sauf si vous codez pour un ancien serveur SGI OpenGL distant.

starmole
la source
4
Faux. Comme dans le commentaire que j'ai laissé dans la réponse ci-dessus: Si vous chargez des ressources (par exemple: textures) sur un thread et que vous les utilisez pour rendre sur un autre, avec partage via wglShareLists ou similaire, vous devez appeler glFinish avant de signaler au thread de rendu qu'il est libre de rendre ce qui a été chargé de manière asynchrone. Et ce n'est pas une non-opération comme tu l'as dit.
Marco Mp
Vraiment? Pouvez-vous confirmer la nécessité d'appeler glFinish avant d'utiliser un objet partagé avec un lien de spécification? Je peux totalement voir un pilote de buggy qui en a besoin. Et ce genre de retour à ce que j'ai dit. Les gens utilisent glFinish pour contourner les pilotes de buggy. En raison de cela, les pilotes qui fonctionnent correctement l'ignorent pour les performances. Le cas d'utilisation de spécification d'origine du profilage (et du rendu en mémoire tampon unique) ne fonctionne plus.
starmole
2
Expérience personnelle d'avoir à faire face à des textures corrompues, plus ceci: Higherorderfun.com/blog/2011/05/26 / ... Si vous y réfléchissez, cela a du sens, cependant, car ce n'est pas très différent d'avoir des mutex ou d'autres synchronisations. api entre les threads - avant qu'un contexte puisse utiliser une ressource partagée, l'autre doit terminer le chargement de cette ressource, d'où la nécessité de s'assurer que le tampon a été vidé. Je pense plus au cas du coin des spécifications plutôt qu'au bogue, mais je n'ai aucune preuve de cette déclaration :)
Marco Mp
Réponse géniale, merci. Surtout "beaucoup d'anciens programmes saupoudraient glFlush et glFinish dans tout leur code sans raison comme vaudou".
akauppi
Est-ce vraiment pas d'opérations? GlFinish et / ou glFlush ne sont-ils pas très utiles pour le traitement d'images haute intensité ou le coprocessage mathématique basé sur GPU? Par exemple, nous ne lisons les résultats du calcul GPU du tampon de pixels vers le CPU qu'après avoir appelé un glFinish pour nous assurer que tous les calculs de pixels ont été écrits. Sommes-nous en train de manquer quelque chose?
Praxitèle
5

Jetez un œil ici . En bref, il dit:

glFinish () a le même effet que glFlush (), avec l'ajout 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 permutation (utilisées dans les applications à double tampon) vident automatiquement les commandes, donc pas besoin d'appeler glFlush
  • glFinish force OpenGL à exécuter des commandes exceptionnelles, ce qui est une mauvaise idée (par exemple avec VSync)

Pour résumer, cela signifie que vous n'avez même pas besoin de ces fonctions lorsque vous utilisez le double tampon, sauf si votre implémentation de swap-buffers ne vide pas automatiquement les commandes.

AndiDog
la source
Oui, mais le document dit que les deux ont le même effet avec seulement quelques différences concernant le système de fenêtres, par exemple. Mais de toute façon j'ai édité ma réponse;)
AndiDog
Bel appel sur la nuance. Il clarifie s'il est nécessaire d'appeler glFlush () et THEN glFinish () - (une question que nous avions après avoir lu tout ce qui précède.)
Praxiteles
4

Il ne semble pas y avoir de moyen d'interroger l'état du tampon. Il y a cette extension Apple qui pourrait servir le même but, mais elle ne semble pas multiplateforme (je ne l'ai pas essayée.) À première vue, il semble avant que flushvous n'appuyiez la commande fence; vous pouvez ensuite interroger l'état de cette clôture lorsqu'elle se déplace dans la mémoire tampon.

Je me demande si vous pouvez utiliser flushavant de mettre en mémoire tampon les commandes, mais avant de commencer à rendre la prochaine image que vous appelez finish. Cela vous permettrait de commencer le traitement de l'image suivante au fur et à mesure que le GPU fonctionne, mais si ce n'est pas fait au moment de votre retour, il finishse bloquera pour s'assurer que tout est à l'état neuf.

Je n'ai pas essayé cela, mais je le ferai sous peu.

Je l'ai essayé sur une ancienne application qui utilise assez même le processeur et le GPU. (Il était utilisé à l'origine finish.)

Quand je l'ai changé flushà la fin et finishau début, il n'y avait pas de problèmes immédiats. (Tout avait l'air bien!) La réactivité du programme a augmenté, probablement parce que le CPU n'était pas bloqué en attendant le GPU. Certainement une meilleure méthode.

A titre de comparaison, j'ai retiré finisheddu début du cadre, en partant flush, et il en a été de même.

Donc, je dirais utiliser flushet finish, car lorsque le tampon est vide à l'appel de finish, il n'y a pas de performance. Et je suppose que si le tampon était plein, vous devriez le vouloir de finishtoute façon.

GManNickG
la source
0

La question est la suivante: voulez-vous que votre code continue de s'exécuter pendant que les commandes OpenGL sont exécutées, ou uniquement après l'exécution de vos commandes OpenGL.

Cela peut être important dans les cas, comme sur les retards du réseau, pour avoir une sortie de console uniquement après que les images ont été dessinées ou autre.

anon
la source