I. Article original

Le blog KDAB est rédigé par les ingénieurs de KDAB s'occupant des formations, de la consultance ainsi que du développement (de Qt et de produits additionnels). Vous pouvez trouver les versions originales.

Cet article est une traduction de l'article original écrit par Sean Harmer paru le 18 mars 2013.

Cet article est une traduction de l'un des articles en anglais écrits par KDAB. Les éventuels problèmes résultant d'une mauvaise traduction ne sont pas imputables à KDAB.

II. Requêtes temporelles

OpenGL propose sur les plates-formes de bureau un outil très pratique nommé requêtes temporelles. Ces derniers permettent de mesurer le temps passé par le processeur graphique à traiter un certain nombre de commandes. En couplant ces outils aux traditionnelles techniques de profilage du processeur, il devient aisé de mettre en évidence les zones d'engorgement du code de rendu.

Une partie qui peut prendre avantage de cela est bien évidemment le moteur de rendu de Qt Quick 2. Bien qu'il possède des options pour le chronométrage de certaines étapes du rendu, cela ne fait pas tout étant donné que le processeur est lui aussi très sollicité. L'utilisation des requêtes temporelles d'OpenGL permettrait de combler le fossé et d'observer la façon dont travaille le processeur graphique sur cette base de code.

Les requêtes temporelles sont aussi utilisées afin d'obtenir un retour quant aux procédures adaptatives de rendu afin de stabiliser le nombre d'images par seconde. Ainsi, si l'on surveille une fonction de rendu et que l'on observe durant l'exécution que cette dernière est trop lente, il est sûrement possible d'augmenter le nombre d'images par seconde en utilisant des shaders moins évolués ou des modèles comportant moins de polygones. Inversement, s'il s'avère que le processeur graphique est sous-utilisé, il est possible d'améliorer le rendu en augmentant le niveau de pavage (tessellation).

Qt 5.1 facilite ces tâches grâce à deux classes : QOpenGLTimerQuery et QOpenGLTimeMonitor. La classe QOpenGLTimerQuery est une simple surcouche des requêtes temporelles et peut être utilisée si vous souhaitez avoir totalement la main. On s'attend à un usage plus fréquent de QOpenGLTimeMonitor étant donné qu'elle encapsule une série de requêtes temporelles. Elle permet de mesurer le temps passé par le processeur graphique à afficher les éléments, et ce à différentes étapes de la fonction de rendu :

 
Sélectionnez
void Scene::initialize()
{
    // Création d'une requête temporelle
    m_timeMonitor = new QOpenGLTimeMonitor( this );
    m_timeMonitor->setSampleCount( 5 );
    if ( !m_timeMonitor->create() )
        qWarning() << "Failed to create timer query object";
 
    // Génère des noms pour les différentes étapes du rendu
    m_renderStages << "Clear Buffer" << "VAO Binding"
                   << "Shader Program Binding" << "Drawing";
}
 
void Scene::render()
{
    // Démarre la requête
    m_timeMonitor->recordSample();
 
    // Un peu de rendu
    m_funcs->glClear( GL_COLOR_BUFFER_BIT );
    m_timeMonitor->recordSample();
    m_vao.bind();
    m_timeMonitor->recordSample();
    m_shaderProgram.bind();
    m_timeMonitor->recordSample();
    m_funcs->glDrawArrays( GL_TRIANGLES, 0, 3 );
 
    // Fin de la requête
    m_timeMonitor->recordSample();
 
    // Blocage jusqu'à ce que les résultats soient disponibles
    QVector<GLuint64> timeSamples = m_timeMonitor->waitForSamples();
    qDebug() << "timeSamples =" << timeSamples;
 
    QVector<GLuint64> intervals = m_timeMonitor->waitForIntervals();
    for ( int i = 0; i <= intervals.count(); ++i )
        qDebug() << i << m_renderStages.at( i ) 
                 << double( intervals.at( i ) ) / 1.0e6 << "msecs";
 
    // Suppression des anciens résultats dans l'optique du traitement des prochaines images
    m_timeMonitor->reset();
}

L'exemple ci-dessus est relativement trivial mais montre le principe. Notez bien que les intervalles et les mesures sont enregistrés en nanosecondes. L'exemple ci-dessus a pour sortie :

 
Sélectionnez
timeSamples = QVector(5850713788736, 5850713794176, 5850713794848, 5850713795552, 5850713801952)
0 "Clear Buffer" 0.00544 msecs
1 "VAO Binding" 0.000672 msecs
2 "Shader Program Binding" 0.000704 msecs
3 "Drawing" 0.0064 msecs

Cet exemple très simple (qui se contente de dessiner un très grand triangle) montre que le GPU n'est utilisé qu'une fraction de milliseconde. Cela signifie que l'on aurait facilement pu lui en demander beaucoup plus (heureusement d'ailleurs !).

Il y aurait beaucoup à dire sur les subtilités quant à l'usage des requêtes temporelles mais ce sera pour le prochain article.

III. Remerciements

Au nom de toute l'équipe Qt, j'aimerais adresser le plus grand remerciement à KDAB pour nous avoir autorisés à traduire cet article !

Je tiens à remercier Thibaut Cuvelier et Alexandre Laurent pour leurs conseils ainsi que Philippe Duval pour leur relecture.