Qt Graphics et performance - Du texte rapide

Image non disponible

Cet article est une traduction autorisée de Qt Graphics and Performance - Fast text de Gunnar.

N'hésitez pas à commenter cet article !
9 commentaires Donner une note à l'article (5)

Article lu   fois.

Les trois auteurs

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. L'article original

Qt Labs est un site géré par les développeurs de Qt. Ils y publient des projets, des idées propres et des composants afin d'obtenir les retours d'informations sur les API, le code et les fonctionnalités ou simplement pour partager avec nous ce qui les intéresse. Le code que vous y trouverez peut fonctionner tel quel mais c'est sans aucune garantie ni support. Voir les conditions d'utilisation pour plus d'informations.

Nokia, Qt, Qt Labs 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 une traduction de l'article Qt Graphics and Performance - Fast text de Gunnar paru dans Qt Labs.

II. Introduction

Dans mon précédent article, The Cost of Convenience, nous avons vu très clairement que le rendu du texte est un goulot d'étranglement. Afficher du texte est assez fréquent dans les applications avec interface graphique donc nous avons besoin d'une solution pour cela. Si l'on décompose ce qui se passe derrière QPainter::drawText(), il est divisé en deux parties distinctes. Convertir la chaîne en un ensemble de symboles positionnés correspond à la mise en forme du texte, c'est-à-dire positionner les symboles, faire l'habillage du texte et faire les ajustements pour l'alignement. La seconde partie est de transmettre les symboles au moteur de rendu pour qu'il puisse les dessiner. Lorsque le texte est le même tout le temps, la première partie peut être faite une fois pour toutes et les symboles et positions simplement réutilisés.

III. QTextLayout

Nous avons une classe dans Qt qui permet de mettre en cache la première partie et de ne faire que le dessin pour chaque image. Cette classe est QTextLayout. Il s'agit d'une classe de bas niveau, qui vous protège des erreurs les plus triviales. Elle dispose également d'une API qui dérange vraiment mais cela permet de réduire l'étape la plus coûteuse de l'élaboration du texte, qui est la partie mise en page. Il est également juste de dire que QTextLayout utilise beaucoup plus de mémoire que de simples tableaux de symboles et de positions, comme on pouvait s'y attendre, alors dans un système ayant des fortes contraintes de mémoire, elle doit être utilisée avec prudence. Dans Qt 4.7, nous avons l'intention d'introduire une API pour le texte statique, qui prend en charge toute la mise en forme et n'enregistre que les parties nécessaires, réduisant l'utilisation mémoire globale mais pour l'instant, vous devez le faire avec QTextLayout.

Pour en revenir à mon clavier virtuel, dont le code source est mis à jour ici, j'ai changé l'exemple ButtonView pour utiliser QTextLayout. Dans le constructeur, je réalise la mise en forme :

 
Sélectionnez
ButtonView() {
    QString content;
    for (int i='0'; i< ='Z'; ++i) {
        content += QLatin1Char(i);
        content += QChar(QChar::LineSeparator);
    }
    m_layout = new QTextLayout(content, font());
    QFontMetricsF fm(font());
    m_layout->beginLayout();
    for (int i=0; i<content .size() / 2; ++i) {
        QTextLine line = m_layout->createLine();
        line.setNumColumns(1);
        int x = (i) % 10;
        int y = (i) / 10;
        QSizeF s = fm.boundingRect(content.at(i*2)).size();
        line.setPosition(QPointF(x * 32, y * 32) + QPointF(16 + s.width() / 2, 16 + s.height() / 2));
    }
    m_layout->endLayout();
    m_layout->setCacheEnabled(true);
}

Si vous regardez le code source, il y a plus de choses qui se passent dans le constructeur que ce que je montre ci-dessus. C'est parce que j'ai extrait seulement la partie pertinente pour la mise en forme du texte. Donc, ce que nous avons à faire est de construire une chaîne de caractères. Entre chaque caractère, j'insère un LineSeparator. Sans cela, je ne serais pas capable de scinder le texte en de multiples objets QTextLine. À partir de la chaîne de caractères content, je réalise la mise en forme. Pour chaque caractère, je trouve sa position dans la grille, je construis un QTextLine et déplace le QTextLine à sa position. Chaque QTextLine correspond à une colonne et donc à un caractère. Enfin j'ai activé la mise en cache sur le QTextLayout. C'est l'étape où nous commençons à mettre en cache le texte mis en forme.

Lorsque l'on arrive à la méthode paint, le code est beaucoup plus simple. Tout le texte est contenu dans un seul objet QTextLayout donc je peux seulement appeler sa fonction de dessin.

 
Sélectionnez
void paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *) {

    // Draw background pixmaps...

    m_layout->draw(p, QPointF(0, 0));
}

IV. Performances

Maintenant, regardons ce que nous avons gagné :

Image non disponible

Le graphique montre le nombre de millisecondes par image, y compris la copie du cache. Mesuré sur un N900 avec la composition désactivée. Plus c'est petit, mieux c'est !

Si on compare les graphiques de no-indexing -optimize-flags avec ceux de -no-indexing -optimize-flags -text-layoutt, nous voyons qu'il y a une réduction significative par image. Cela permet au moteur Raster de passer de 9,3 ms par image à 5,5 ms et à OpenGL de passer de 16 ms par image à 9,1 ms pour du texte avec mise en forme. Une baisse d'environ 4 ms est également visible pour le moteur de rendu X11.

Inutile de le dire, utiliser la classe QTextLayout présente un énorme avantage mais elle nécessite une meilleure configuration pour fonctionner. Dans cette implémentation, j'ai fusionné tout le texte en un seul objet, ce qui rend également impossible de déplacer un élément par rapport aux autres, comme l'ajout d'un décalage quand un bouton est pressé. Je pourrais avoir un QTextLayout pour chaque élément, ce qui aurait été à peu près pareil pour les mêmes performances mais avec un coût plus élevé pour la mémoire.

Pour la prochaine fois, prenez garde !

V. Conclusion

Un petit commentaire sur les chiffres obtenus avec -item-cache sur X11. La connexion est asynchrone et Qt termine son travail en environ 2,7 ms par image. Avec l'option -sync en ligne de commande, qui rend tous les appels à X11 synchrones, le temps augmente et passe à environ 10 ms par image. Si j'ajoute un QApplication::syncX() à chaque image, ce qui permet de synchroniser à chaque image, ce que font OpenGL et OpenVG, je devrais probablement obtenir des chiffes qui se situent entre les deux. Ce qui veut dire que les chiffres pour X11 sont en fait légèrement plus élevés que ce que les graphiques montrent.

VI. Divers

Merci à Benj., à ClaudeLELOUP et à jacques_jean pour leur relecture et leurs conseils !

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

Qt Graphics et performances
Ce qui est critique et ce qui ne l'est pas
Vue d'ensemble
Le moteur de rendu Raster
Le moteur de rendu OpenVG
Le moteur de rendu OpenGL
Le coût des commodités
Du texte rapide
Génération de contenu dans des threads
La folie est de mettre en forme le même texte encore et espérer un résultat différent
Velours et QML Scene Graph
  

Copyright © 2010 Gunnar. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.