J'ai une application de test OpenGL simple en C qui dessine différentes choses en réponse à une entrée clé. (Mesa 8.0.4, essayé avec Mesa-EGL et avec GLFW, Ubuntu 12.04LTS sur un PC avec NVIDIA GTX650). Les tirages sont assez simples / rapides (type de truc en triangle tournant). Mon code de test ne limite pas le framerate délibérément, il ressemble à ceci:
while (true)
{
draw();
swap_buffers();
}
J'ai chronométré cela très soigneusement, et je trouve que le temps d'un appel eglSwapBuffers()
(ou de la glfwSwapBuffers
même chose) au suivant est d'environ 16,6 millisecondes. Le temps qui s'écoule entre un appel et eglSwapBuffers()
juste avant le prochain appel n'est que légèrement inférieur, même si ce qui est dessiné est très simple. Le temps nécessaire à l'appel des tampons d'échange est bien inférieur à 1 ms.
Cependant, le temps écoulé entre l'application qui change ce qu'elle dessine en réponse à la pression de la touche et le changement qui apparaît réellement à l'écran est> 150 ms (valeur d'environ 8 à 9 images). Ceci est mesuré avec un enregistrement par caméra de l'écran et du clavier à 60 images par seconde.
Par conséquent, les questions:
Où les tirages sont-ils mis en mémoire tampon entre un appel à échanger des tampons et apparaître réellement à l'écran? Pourquoi le retard? Il semble que l'application dessine à tout moment de nombreuses images devant l'écran.
Que peut faire une application OpenGL pour provoquer un tirage immédiat à l'écran? (c'est-à-dire: pas de mise en mémoire tampon, juste bloquer jusqu'à ce que le tirage soit terminé; je n'ai pas besoin d'un débit élevé, j'ai besoin d'une faible latence)
Que peut faire une application pour que le tirage immédiat ci-dessus se produise le plus rapidement possible?
Comment une application peut-elle savoir ce qui est actuellement affiché à l'écran? (Ou, combien de temps / combien d'images le délai de mise en mémoire tampon actuel est-il?)
Réponses:
Toute fonction d'API de dessin appelée à partir du CPU sera soumise au tampon d'anneau de commande du GPU pour être exécutée ultérieurement par le GPU. Cela signifie que les fonctions OpenGL sont principalement des fonctions non bloquantes. Ainsi, le CPU et le GPU fonctionneront en parallèle.
La chose la plus importante à noter est que votre application peut être liée au CPU ou au GPU. une fois que vous appelez glFinish, le processeur attendra que le GPU termine ses commandes de dessin, si le GPU prend plus de temps et peut / provoque le blocage du processeur, vos applications sont liées au GPU. Si le GPU termine ses commandes de dessin et que le CPU prend trop de temps à glFinish, votre application est liée au CPU.
Et notez qu'il y a une différence entre
glFlush
etglFinish
.glFlush
: indique que toutes les commandes qui ont été précédemment envoyées au GL doivent se terminer en un temps fini.glFinish
: force toutes les commandes GL précédentes à se terminer. Terminer ne revient que lorsque tous les effets des commandes précédemment émises sur l'état du client et du serveur GL et sur le framebuffer sont pleinement réalisés. "glXSwapBuffers effectue un glFlush implicite avant son retour. Les commandes OpenGL suivantes peuvent être émises immédiatement après l'appel de glXSwapBuffers, mais ne sont pas exécutées tant que l'échange de tampon n'est pas terminé.
Le temps de trame réel sera très probablement déterminé par lequel des deux CPU / GPU prend plus de temps pour terminer son travail.
la source
both low framerate (exactly same as monitor refresh rate??
non, sauf si vous utilisez explicitement VSync.Techniquement, OpenGL ne met jamais à jour l'écran.
Il existe une API de système de fenêtres distincte de GL (par exemple GLX, WGL, CGL, EGL) qui fait cela. Les échanges de tampons utilisant ces API invoquent généralement implicitement
glFlush (...)
mais dans certaines implémentations (par exemple le rasterizer GDI sur Windows), il fait un pleinglFinish (...)
:* Sur le côté gauche, se trouve le chemin ICD (matériel)
SwapBuffers (...)
. Sur le côté droit, le chemin GDI (logiciel).Si vous avez activé VSYNC et la double mise en mémoire tampon, toute commande qui modifierait le tampon arrière avant qu'un échange en attente ne se produise doit se bloquer jusqu'à ce que l'échange. Il y a une profondeur limitée dans la file d'attente de commandes, donc cette commande bloquée finira par provoquer un embouteillage dans le pipeline. Cela pourrait signifier qu'au lieu de bloquer
SwapBuffers (...)
votre application, il bloque en fait certaines commandes GL non liées jusqu'à ce que VBLANK se déplace. Cela se résume vraiment au nombre de tampons en arrière que vous avez dans votre chaîne de swap.Tant que tous les tampons arrière sont pleins d'images finies qui n'ont pas encore été déplacées vers l'avant, les tampons d'échange entraîneront implicitement un blocage. Malheureusement, il n'y a aucun moyen de contrôler explicitement le nombre de tampons arrière utilisés par la plupart des API du système de fenêtres GL (à part 0 à tampon unique ou 1 à double tampon). Le pilote est libre d'utiliser 2 tampons arrière s'il le souhaite (triple tampon), mais vous ne pouvez pas le demander au niveau de l'application en utilisant quelque chose comme GLX ou WGL.
la source
glFinish (...)
immédiatement après l'échange des tampons. Cela effacera la file d'attente de commandes, mais cela signifie également que le GPU et le CPU seront synchronisés (ce qui n'est pas bon si vous souhaitez que le GPU fonctionne à tout moment).Je suppose que vous connaissez cette expérience ?
Essentiellement, John Carmack faisait quelque chose de similaire, enregistrant l'écran et chronométrant les pixels envoyés à l'écran. Il a constaté qu'une grande partie de la latence provenait de l'écran. D'autres facteurs étaient le retard d'entrée du clavier, les pilotes vidéo et / ou bien sûr l'exécution du programme lui-même.
la source