Bienvenue sur le forum !

Si vous souhaitez rejoindre la communauté, cliquez sur l'un de ces boutons !

Qt : 5.11.1 - Qt Creator : 4.7.0 - Qt Installer : 3.0.4 - JOM : 1.1.2 - QBS : 1.12.0

QGLWidget repaint

J'ai un petit souci avec QGLWidget (Qt version 4.1) :

Lorsque je déplace une dialogue sur le QGLWidget un updateGL() se déclenche. Cela provoque le ralentissement de l'application car le déplacement déclenche une floppée de rafraichissements et donc d'appels à paintGL().
Ces appels à paintGL() me semblent inutiles puisque la scene GL n'a pas changé...
Une simple gestion du widget en tant que bitmap duquel il ne faudrait restorer que la petite partie manquante serait suffisante (comme tous les autres widgets). Et lorsqu'un vrai changement de la scene GL intervient on peut faire appel à updateGL() pour forcer le rafraichissement du bitmap.

Qt ne semble pas gérer QGLWidget de cette manière, une idée ?

Réponses

  • silbes said:
    Ces appels à paintGL() me semblent inutiles puisque la scene GL n'a pas changé...
    Il faut quand même repeindre les endroits quittés par le dialogue, que la scène ait changé ou pas. En effet OpenGL ne maintient pas de cache du rendu de la scène (technique de double-buffer).
    silbes said:
    Une simple gestion du widget en tant que bitmap duquel il ne faudrait restorer que la petite partie manquante serait suffisante (comme tous les autres widgets). Et lorsqu'un vrai changement de la scene GL intervient on peut faire appel à updateGL() pour forcer le rafraichissement du bitmap.

    Qt ne semble pas gérer QGLWidget de cette manière, une idée ?
    QGLWidget ne peut rien gérer car c'est OpenGL qui gère le rendu directement. Qt n'est pour rien dans tout ça. C'est à l'utilisateur d'OpenGL d'éventuellement effectuer le rendu dans une pixmap - puis d'afficher la pixmap dans un QWidget quelconque.
  • Salut,

    Regarde du côté de QGLFramebufferObject et/ou QGLPixelBuffer (selon ton contexte (non, pas de rendu, de projet :P )).
    En combinant l'un d'entre eux à un flag disant si oui ou non la scène a été modifiée, tu peux t'économiser les repaint inutile, ou plus exactement, les réduire quasiment à néant.

    L'idée est plutôt de tout rendre dans ton QGLWidget, tu rends tout dans une texture (créée donc à l'aide de l'une des 2 classes pré-citées), et tu appliques cette texture à une surface qui recouvre complétement ta fenêtre. C'est un espèce de double buffer matériel, qui te permet de faire du post-process à moindre coûts dans le cas où tu peux en avoir besoin ;)
  • À noter que ces buffers ne sont à ma connaissance que rarement disponibles sous Linux - ils sont disponibles avec les drivers Nvidia propriétaires entre autres.
  • Arg, j'aurais du m'en douter :(
  • J'étais parti quelques jours, merci pour vos réponses... mais mon problème n'est pas résolu.

    Je comprends bien où est le problème Dimitri... je ne peux donc pas compter sur Qt pour régler ce souci à moins de faire quelque chose se basant sur l'idée d'IrmatDen.
    Donc le plus simple serait d'optimiser mon affichage 3D. Ce qui m'étonne c'est que ce dernier n'est pas très compliqué et j'utilise des Display List pour accèlerer le rendu des parties quelques peu complexes.
    De plus la navigation dans la scène est extrêmement fluide, le problème se pose uniquement lorsque je déplace une dialogue sur le QGLWidget.
  • Mais quid de la solution proposée par Dimitri? Utiliser QGLWidget::renderPixmap te permet d'obtenir l'image de ta scène; ça t'oblige par contre à penser l'interaction différemment par contre.
  • Oui, quand je parlais de ta solution j'entendais aussi celle de Dimitri, le principe est un peu le même si ce n'est que la tienne est essentiellement OpenGL alors que celle de Dimitri est plutôt Qt.
    Je viens d'essayer mon programme sur un PC un peu plus puissant (j'avais noté le pb sur un portable) et le problème est bien moins visible, du coup cela devient moins important.
    Du coup je ne vais pas implémenter cela mais pour aller au bout de la discussion, voici ce que j'aurais tenté de faire :

    - Donner à ma classe Scene un moyen de savoir si la scene à changé ou non.
    - Créer une classe dérivant de QGLWidget MyGLWidget et lui ajouter un QPixmap (m_pixmap) et un flag de validité du pixmap.
    - Surcharger paintEvent de MyGLWidget :
    Si la scene à changé : appeler paintEvent de QGLWidget et invalider m_pixmap
    Sinon :
    Si m_pixmap n'est pas valide : appeller m_pixmap = renderPixmap(...) et valider m_pixmap.
    Faire le rendu du pixmap à l'aide d'un QPainter (uniquement la partie dégradée, si possible).

    Je pense que l'interaction pourrait continuer à se faire comme maintenant à l'aide des fonction Mouse[...]Event.

    Cela parait jouable ?
  • Oui c'est l'idée. Juste pour précision (au cas où, mais tu t'en doutais probablement :)), lors de la surcharge de paintEvent, n'appelle pas l'implémentation de la classe de base. Sinon, ça appellera paintGL et tu gaspilleras un peu (ou pas :)) de ressources pour rien.
  • IrmatDen said:
    Oui c'est l'idée. Juste pour précision (au cas où, mais tu t'en doutais probablement :)), lors de la surcharge de paintEvent, n'appelle pas l'implémentation de la classe de base. Sinon, ça appellera paintGL et tu gaspilleras un peu (ou pas :)) de ressources pour rien.
    Je pense que tu fais référence à :
    silbes said:
    Si la scene à changé : appeler paintEvent de QGLWidget et invalider m_pixmap
    L'idée de ma procédure est d'utiliser directement OpenGL lorsque la scène change en permanence, c'est à dire quand on navigue dans la scène (shift, rotate ou zoom) et d'utiliser le QPixmap lorsque la scene ne change pas mais que le widget doit être rafraîchi.
    Je pense qu'utiliser le QPixmap en permanence sera moins rapide que le rendu OpenGL issu de QGLWidget::paintEvent(...), c'est pour cela que je souhaite faire ainsi.
  • Je fais référence à ça, mais pas de la façon dont tu le cite... ou alors, on est pas sur la même longueur d'onde :)

    Plus clair, je fais référence à ça (de la doc de QGLWidget::paintEvent :
    Handles paint events passed in the event parameter. Will cause the virtual paintGL() function to be called.
    Ce qui signifie que si tu appelles l'implémentation de la classe de base (dans le cas où la scène n'a pas changer), tu vas bouffer du cpu pour rien, voire probablement corrompre ton affichage.

    Ca donnera à peu près ça:
    void MyGLWidget::paintEvent(QPaintEvent *e)
    {
    if(dirtyPixmap)
    pixmap = renderPixmap();

    QPainter p(this);
    p.drawPixmap(pixmap);

    e->accept();
    }
    En gros, aucune ligne QGLWidget::paintEvent(e) dans la réimplémentation.
  • Si, si j'avais bien compris ton message.

    Je me suis peut-être mal exprimé dans mon dernier message concernant les flags : il y en a 2, un pour la scene et un pour le pixmap.

    Lorsque je la scène n'est pas modifiée je n'ai pas besoin de faire le rendu OpenGL et il vaut mieux utiliser le QPixmap pour accélerer le rafraîchissement du widget.

    Lorsque je modifie la scène (changement du point de vue), j'ai forcément besoin de rafraîchir le rendu OpenGL, et je pense que créer un QPixmap à chaque fois et faire le rendu dans le widget sera plus long que de laisser OpenGL faire le rendu directement à l'aide de paintEvent(e)
    void MyGLWidget::paintEvent(QPaintEvent *e)
    {
    if(scene->isModified())
    paintEvent(e);
    else
    {
    if(dirtyPixmap)
    pixmap = renderPixmap();

    QPainter p(this);
    p.drawPixmap(pixmap);
    }

    e->accept();
    }
    Au final ce n'est pas très compliqué ; si j'ai 5 minutes je tenterai d'implémenter cela demain.
    Merci !
  • Dans le code, il te manque jste la mise à jour du pixmap dans le cas d'une modification de la scène ;)
    C'est ce que j'ai signifié par 'dirtyPixmap' en fait :)
    Ca te permettra toujours de virer quelques lignes, vu qu'au final le pixmap est rendu quand même.
Connectez-vous ou Inscrivez-vous pour répondre.