I. L'article original▲
Le site Qt Labs permet aux développeurs de Qt de présenter les projets, plus ou moins avancés, sur lesquels ils travaillent.
Nokia, Qt, Qt Quarterly et leurs logos sont des marques déposées de Nokia Corporation en Finlande et/ou dans les autres pays. Les autres marques déposées sont détenues par leurs propriétaires respectifs.
Cet article est la traduction de l'article Velvet and the QML Scene Graph de Gunnar paru dans Qt Labs.
II. Introduction▲
Tout d'abord, un peu de clarification. Aux DevDays cette année, beaucoup de personnes ont remarqué que le nom du projet, scene graph, était mal choisi. À cause d'une similarité dans le nom, il donnait impression de similarité avec des projets comme Open Scene Graph, ce qui n'est ni le cas, ni l'intention. Notre graphe de scène est un graphe 2D de petite taille et assez compact pour le rendu de fichiers XML. Ainsi, dès maintenant, nous y ferons référence comme au "QML Scene Graph". Son seul but dans l'existence est de rendre QML encore meilleur.
III. Les animations▲
Les animations devraient plus ressembler à du velours : une douce homogénéité plaisante. Du point de vue technique, cela requiert quelques petites choses.
Dessiner une image chaque fois que l'affichage le permet. Des moniteurs modernes, comme les écrans LCD ou LED que vous utilisez pour lire ceci, sont pour la plupart réglés sur 60 Hz. Cela dépend de leur résolution (dpi), bien évidemment, mais des choses assez magiques peuvent avoir lieu aux alentours de 60 Hz. Si on met à jour des graphiques 2D à 30 Hz (soit trente fois par seconde), on peut voir chaque image comme une image individuelle. Dès qu'on se rapproche de 60 Hz, elles commencent à fusionner et les yeux perçoivent un mouvement fluide au lieu d'images juxtaposées, voilà la grande différence entre le velours et du papier de sable.
Être prête à temps. Pour atteindre 60 Hz, on doit calculer chaque image en au plus 16,66 millisecondes. Si on dépasse ce temps, on a raté le coche. Ce qui signifie que, pendant des animations, on ne peut pas faire autre chose que mettre à jour quelques propriétés puis dessiner le tout. Si ça prend du temps, cela nécessite soit d'être prêt au préalable, soit d'être fait dans un thread en arrière-plan, comme décrit dans l'article précédent. Jusqu'il y a peu, j'étais convaincu que rater le coche l'une ou l'autre fois ne serait pas désastreux, mais ça fait vraiment une différence, comme entre le velours et le papier de sable.
Ne pas dessiner en plein milieu d'un rafraîchissement vertical, cela mène à une déchirure d'écran, soit, d'une manière simple, votre image est découpée en deux, ce qui ruine ce qui aurait dû être un pur moment de plaisir visuel. La solution est d'utiliser une forme de synchronisation qui lie votre application et le taux de rafraîchissement vertical de l'écran, Qt ne vous y aide pas toujours. Sur Mac OS X et Symbian N8, le système vous limite à une fréquence de rafraîchissement vertical ; toute image que l'on tente de dessiner en plus provoque un blocage du thread de rendu par le système de fenêtrage. Sur Linux/X11, Maemo et Windows, il n'y a aucun blocage, ce phénomène peut donc apparaître. Heureusement, il y a une solution relativement simple. QGLWidget combiné à QGLFormat::setSwapInterval mis à 1 autorisera à ce widget la synchronisation verticale. Dans le QML Scene Graph, nous requérons OpenGL pour définir l'intervalle d'échange à 1 par défaut.
L'avance de l'animation, relativement au temps entre chaque image. Si votre objet bouge à la vitesse d'un pixel par milliseconde, alors il doit bouger de 16,66 pixels par image. Si une image n'est pas dessinée, alors il doit bouger de 33,33 pixels la prochaine fois. Évidemment, si vous atteignez toujours l'objectif des 16,66 ms, ce problème n'en est pas un.
IV. Et pourtant... ▲
Même avec toutes ces précautions, Qt ne vous garantit pas encore le velours.
L'ingrédient manquant est l'endroit d'où le déclenchement de l'animation vient. Le framework Animation de Qt utilise un timer, qui avance les animations et met à jour les propriétés de tous les objets. Il envoie plusieurs requêtes de mise à jour et, finalement, l'image est redessinée. Il y a deux problèmes dans ce système. D'abord, le timer se déclenche toutes les 1000 / 60 ms, ce qui est arrondi à 16, ce qui n'est pas 16,66 comme cela aurait dû l'être. Ensuite, le timer n'est pas précis. Il peut se déclencher à 15 comme à 17 ; s'il se déclenche à 17, l'animation aura raté le coche une fois, une image ne sera pas dessinée.
Ceci m'a inquiété pendant un certain temps ; ainsi, pendant les DevDays de San Francisco, j'ai eu un peu de temps pour creuser le sujet. Le résultat est que le QML Scene Graph conduira les animations d'une manière un peu différente.
while
(animationIsRunning) {
processEvents();
advanceAnimations();
paintQMLScene();
swapAndBlockForNextVSync();
}
Le résultat est qu'on progresse toujours dans l'animation en synchronisation avec le rafraîchissement vertical ; ainsi, on dessine une image toutes les 16,66 ms, soit très exactement à chaque fois qu'on peut en afficher une. J'ai dit que je n'étais pas un convaincu de la première heure, qu'on pouvait rater de temps en temps une image. Un seul coup d'œil au résultat. J'ai su qu'on y était. Le velours.
Nous ne pouvons pas faire ça généralement dans Qt, car cette méthode pour la synchronisation verticale ne se fait que par la fonction swapBuffers() d'OpenGL. On ne peut l'utiliser que pour une seule fenêtre. Avec Wayland ou des extensions personnalisées d'OpenGL, on pourrait potentiellement avoir la synchronisation verticale sans devoir échanger, ce qui signifie que, en théorie, on pourrait avancer dans les animations sur plusieurs fenêtres, mais c'est hors de portée pour le moment. Actuellement, c'est fixé pour une seule fenêtre utilisant le QML Scene Graph.
V. Conclusion▲
Au nom de toute l'équipe Qt, j'aimerais adresser le plus grand remerciement à Nokia pour nous avoir autorisé la traduction de cet article !
Merci à jacques_jean pour sa relecture !