

/*! \addtogroup time 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](http://www.cplusplus.com/reference/chrono/) pour mesurer le temps d'exécution sur cpu. 
Le principe est simple : 
    - relever l'heure courante, 
    - exécuter la fonction dont on veut mesurer le temps, 
    - relever l'heure à la fin de l'exécution,
    - calculer la différence.
    
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`.

\code
#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();
}
\endcode

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 :

\code
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 );
}
\endcode

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 ;

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

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 :
\code
    int milli= (int) (gpu_time / 1000000);
    int micro= (int) ((gpu_time / 1000) % 1000);
    printf("gpu  %02dms %03dus", milli, micro);
\endcode


__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 :
    - mode 1 : affiche 25 fois l'objet en modifiant sa transformation model, pour placer les 25 copies sur une grille,
    - mode 2 : utilise glDrawArraysInstanced( ) pour faire la même chose mais avec un seul draw... le travail pour le gpu est le meme : 
    25 fois plus de sommets à transformer et de triangles à dessiner, mais c'est le vertex shader qui calcule la position de chaque copie,
    cf \ref draw pour les explications.
    
bilan : 
    - mode 0 : 1 draw, temps cpu 50us, gpu 6us
    - mode 1 : devrait prendre 25 fois plus de temps ? temps cpu 500us, gpu 100us
    - mode 2 : ?? temps cpu 40us, gpu 80us

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).

*/
