gKit2 light
mesure du temps cpu et gpu

cf tuto_time.cpp

il y a toujours plusieurs solutions pour dessiner des objets, et chacune présente des avantages (simple à écrire) et des inconvénients (trop lente...), une manière de sélectionner la "meilleure" solution est donc nécessaire. Un critère de sélection est le temps d'exécution.

l'application, la librairie et le driver openGL fonctionnent sur le cpu et le pipeline graphique sur le gpu, de manière asynchrone. Par exemple, un appel à glDrawArrays( ) n'est pas bloquant, l'application continue à s'exécuter sans attendre que le gpu commence à dessiner. Il est donc nécessaire de mesurer le temps cpu et le temps gpu... un seul appel à glDrawArrays( ) peut faire dessiner des millions de triangles ou un seul... par contre, l'application, la librairie et le driver feront le meme travail dans les 2 cas. Il est tout à fait possible que le temps cpu soit plus important que le temps gpu...

mesure temps cpu

il suffit d'utiliser les fonctionnalités de std::chrono du c++ 11 pour mesurer le temps d'exécution sur cpu. Le principe est simple :

la seule subtilité est la précision necéssaire pour faire les mesures : pour le cpu c'est de l'ordre de la micro seconde... et il faut choisir l'horloge la plus précise std::chrono::high_resolution_clock.

#include <chrono>
{
// temps courant, avant l'execution
std::chrono::high_resolution_clock::time_point a= std::chrono::high_resolution_clock::now();
// faire quelquechose
{ ... }
// temps courant, apres l'execution
std::chrono::high_resolution_clock::time_point b= std::chrono::high_resolution_clock::now();
// mesurer la difference, et l'exprimer en microsecondes
unsigned int time= std::chrono::duration_cast<std::chrono::microseconds>(b - a).count();
}

l'utilisation est la même dans une application openGL.

remarque : std::high_resolution_clock est buggé sur visual studio avant la version 2015.

mesure temps gpu

Pour mesurer le temps gpu, il faut demander à openGL de le faire et de transmettre le résultat. C'est un objet "query" qui permet de stocker ces mesures et glBeginQuery() / glEndQuery() qui permettent de faire la mesure. L'utilisation est très proche de la version cpu, mais il faut bien sur créer un objet query avant toute chose :

GLuint time;
int init( )
{
glGenQueries(1, &time);
}
int quit( )
{
glDeleteQueries(1, &time);
}
int draw( )
{
// temps courant, avant l'execution
glBeginQuery( GL_TIME_ELAPSED, time );
// dessiner quelquechose
{ ... }
// temps courant, apres l'execution
glEndQuery( GL_TIME_ELAPSED );
}
int init(std::vector< const char * > &options)
Definition: shader_kit.cpp:96

Les requêtes GL_TIME_ELAPSED mesurent directement la durée entre BeginQuery et EndQuery. Il est aussi possible de recupérer le temps courant avec glQueryCounter( ), mais il faut qu'une requête GL_TIME_ELAPSED soit en cours...

Il ne reste plus qu'à récupérer le résultat avec glGetQueryObject( ). openGL mesure le temps en nanosecondes et renvoie le resultat comme un entier 64 bits, il faut donc utiliser un GLint64 pour récupérer le résultat ;

GLint64 gpu_time= 0;
glGetQueryObjecti64v(time, GL_QUERY_RESULT, &gpu_time);

et il n'y a plus qu'à convertir la durée dans une unité un peu plus "lisible" avant de l'afficher, par exemple en millisecondes et microsecondes :

int milli= (int) (gpu_time / 1000000);
int micro= (int) ((gpu_time / 1000) % 1000);
printf("gpu %02dms %03dus", milli, micro);
void printf(Text &text, const int px, const int py, const char *format,...)
affiche un texte a la position x, y. meme utilisation que printf().
Definition: text.cpp:140

attention : il faut attendre que le gpu ait fini de dessiner pour connaitre la durée, et cette attente bloque le cpu ! en pratique ca ne pose pas de problème, si glGetQueryObject( ) est utilisé après avoir tout dessiné. tuto_time.cpp mesure aussi ce temps d'attente cpu. pour l'affichage d'une scène simple, il est de l'ordre de 16 millisecondes.

et alors ?

tuto_time.cpp affiche un objet "normalement" et compare ensuite les temps de rendu cpu/gpu pour 2 manières différentes d'afficher 25 objets (identiques) disposés sur une grille 5x5 :

bilan :

que peut on en déduire ? premièrement que faire 25 appels à glDrawArrays() (cf mode 1) est très couteux pour le cpu, plus que pour le gpu !! selon les performances de la machine, ce sera soit le cpu soit le gpu qui limitera le nombre d'objets que l'on peut dessiner. et que le gpu peut faire l'equivalent de manière plus efficace (mode 2 plus performant que mode 1, pour le cpu bien sur, mais egalement pour le gpu).

la classe AppTime utilise ces mesures pour afficher automatiquement le temps cpu et le temps gpu.